2013-04-09 3 views
2

В Go вы можете передавать функции как параметры, такие как callFunction(fn func). Например:Использование имен функций в качестве параметров

package main 

import "fmt" 

func example() { 
    fmt.Println("hello from example") 
} 

func callFunction(fn func) { 
    fn() 
}  

func main() { 
    callFunction(example) 
} 

Но можно ли вызвать функцию, когда она является членом структуры? Следующий код потерпит неудачу, но дает вам пример того, что я говорю о:.

package main 

import "fmt" 

type Example struct { 
    x int 
    y int 
} 

var example Example 

func (e Example) StructFunction() { 
    fmt.Println("hello from example") 
} 

func callFunction(fn func) { 
    fn() 
}  

func main() { 
    callFunction(example.StructFunction) 
} 

(я знаю, что я пытаюсь сделать в этом примере является немного странным точной проблема, я Безразлично Однако я также заинтригован об этом с научной точки зрения).

ответ

8

Методы (которые не являются «членами структуры», но методы любого именованного типа, а не только структуры) являются значениями первого класса. Go 1.0.3 еще не реализовал значения метода, но версия подсказки (как в Comm 1.1 Go) поддерживает method values. Цитирование полного раздела здесь:

Метод значение

Если выражение x имеет статический тип T и M находится в методе наборе типа T, x.M называется значением методы. Значение метода x.M - это значение функции, вызываемое с теми же аргументами, что и вызов метода x.M. Выражение x оценивается и сохраняется при оценке значения метода; сохраненная копия затем используется в качестве получателя в любых вызовах, которые могут быть выполнены позже.

Тип T может быть интерфейсом или не-интерфейсом.

Как и в обсуждении метода выражений выше, рассмотрим тип структуры T с двумя методами, Mv, чей приемник имеет тип T и Mp, чей приемник имеет тип *T.

type T struct { 
    a int 
} 

func (tv T) Mv(a int) int   { return 0 } // value receiver 
func (tp *T) Mp(f float32) float32 { return 1 } // pointer receiver 

var t T 
var pt *T 
func makeT() T 

Выражение

t.Mv 

дает функцию значение типа

func(int) int 

Эти два вызова эквивалентны:

t.Mv(7) 
f := t.Mv; f(7) 

Аналогично, выражение

pt.Mp 

дает функцию значение типа

func(float32) float32 

Как с селекторами, ссылка на способ не-интерфейса с приемником значения, используя указатель будет автоматически разыменовать этот указатель: pt.Mv эквивалентно (*pt).Mv ,

Как и при вызове метода, ссылка на метод неинтерфейса с приемником указателя с использованием адресного значения автоматически принимает адрес этого значения: t.Mv эквивалентен (&t).Mv.

f := t.Mv; f(7) // like t.Mv(7) 
f := pt.Mp; f(7) // like pt.Mp(7) 
f := pt.Mv; f(7) // like (*pt).Mv(7) 
f := t.Mp; f(7) // like (&t).Mp(7) 
f := makeT().Mp // invalid: result of makeT() is not addressable 

Хотя в приведенных выше примерах используются типы неинтерфейса, также право создавать значение метода из значения типа интерфейса.

var i interface { M(int) } = myVal 
f := i.M; f(7) // like i.M(7) 
1

Я исправил свои ошибки компиляции.

package main 

import "fmt" 

type Example struct { 
    x, y float64 
} 

var example Example 

func (e Example) StructFunction() { 
    fmt.Println("hello from example") 
} 

func callFunction(fn func()) { 
    fn() 
} 

func main() { 
    callFunction(example.StructFunction) 
} 

Выход:

hello from example 
+0

Я не пытался скомпилировать этот код, это был макет моей проблемы, которую я закодировал непосредственно в SO. Таким образом, ошибки типа package/program и x/y просто отсутствуют, а не проблема с моим исходным кодом. Я озадачен тем, как вы получили результат, потому что ваш код не срабатывает так же, как и мой: 'method example.StructFunction не является выражением, должно быть вызвано' [edit] ahh проблема - это версия Go I'm running (1.0.3) не поддерживает его. Совет. – laumars

+0

'$ go version' ' go version devel + 1a196137ed09 Вт апр 09 18:17:55 2013 +1000 linux/amd64' Означает ли это, что Go теперь делает то, что вы хотите? – peterSO

+0

Пока нет, но когда 1.1 выталкивает мои репозитории, то это будет. Учитывая, что всего за пару недель за углом, я рад ждать. – laumars

2

Go 1.0 не поддерживает использование связанных методов в качестве значений функции. Он будет поддерживаться в Go 1.1, но до этого вы можете получить подобное поведение через закрытие. Например:

func main() { 
    callFunction(func() { example.StructFunction() }) 
} 

Это не совсем так удобно, так как вы в конечном итоге дублирования прототип функции, но должны сделать трюк.

Смежные вопросы