Вызов функции с помощью Go Reflect
Мне было интересно, можно ли не знать имя функции, но в любом случае вызвать его и получить из него значения. Это привело меня к пакету рефлексии, и я подошел довольно близко, но я не уверен насчет последнего шага - если он есть. Снова, пожалуйста, прости меня, если я упускаю что-то очевидное, это моя первая попытка сделать что-нибудь в Go, кроме как настроить его.
Конечно, будучи скомпилированным языком, не нужно перебирать вещи, чтобы найти имена функций, я знаю их все, но это то, что я хочу увидеть, если это возможно... Я играю и учусь.
Ниже приведен код. Что я действительно хотел бы сделать, так это в главной строке извлечь значения, установленные в ModuleBoot() <"1,0012", 23> и SomethingBoot() <"1,0000", 10>, но пока все, что я могу получить, - это информация о структуре. Возможно, так оно и есть, но, возможно, есть шаг или изменение, которое может сделать его следующим шагом.
Надеюсь, я правильно скопировал весь соответствующий код, чтобы он компилировался как есть:
// Using: go version go1.9.7 linux/amd64
=======================================
FILE: main.go
=======================================
package main
import (
"fmt"
"reflect"
"playing/modules/core"
)
func main() {
miType := reflect.TypeOf(core.ModuleInfo{})
fmt.Println("")
for i := 0; i < miType.NumMethod(); i++ {
method := miType.Method(i)
fmt.Println(method.Name)
in := make([]reflect.Value, method.Type.NumIn())
in[0] = reflect.ValueOf(core.ModuleInfo{})
//fmt.Println("Params in:", method.Type.NumIn(), "Params out:", method.Type.NumOut())
mi := method.Func.Call(in)
fmt.Println("mi:", mi)
fmt.Println("")
}
}
=======================================
FILE: playing/modules/core/something.go
=======================================
package core
func (mi ModuleInfo) SomethingBoot() ModuleInfo {
mi.Version = "1.0000"
mi.Priority = 10
return mi
}
=======================================
FILE: playing/modules/core/modules.go
=======================================
package core
type ModuleInfo struct {
Version string
Priority int
}
func (mi ModuleInfo) ModuleBoot() ModuleInfo {
mi.Version = "1.0012"
mi.Priority = 23
return mi
}
Вывод, который я получил от этого был:
Started delve with config "Debug"
SomethingBoot
mi: [<core.ModuleInfo Value>]
ModuleBoot
mi: [<core.ModuleInfo Value>]
delve closed with code 0
2 ответа
Чтобы получить возвращаемое значение в виде ModuleInfo, получите базовое значение первого возвращаемого значения и введите assert это значение интерфейса в ModuleInfo:
// mi has type core.ModuleInfo
mi := method.Func.Call(in)[0].Interface().(core.ModuleInfo)
Запустите его на детской площадке.
Вы можете вырезать часть отражающего кода, введя метод в функцию с правильной сигнатурой и вызвав эту функцию напрямую:
for i := 0; i < miType.NumMethod(); i++ {
method := miType.Method(i).Func.Interface().(func(core.ModuleInfo) core.ModuleInfo)
mi := method(core.ModuleInfo{})
fmt.Println("Version", mi.Version)
fmt.Println("Priority", mi.Priority)
fmt.Println("")
}
Go изначально поддерживает функции как значения; вам не нужно отражение, чтобы сделать это.
В частности, если вы сделаете ваши две функции функциями верхнего уровня (не привязанными конкретно к структуре):
package core
type ModuleInfo struct { ... }
func SomethingBoot() ModuleInfo
func ModuleBoot() ModuleInfo
Затем вы можете написать функцию, которая принимает функцию в качестве параметра:
func PrintVersion(func booter() core.ModuleInfo) {
mi := booter()
fmt.Printf("version %s\n", mi.Version)
}
И вы можете просто передать уже существующие функции в качестве параметров:
PrintVersion(core.SomethingBoot)
PrintVersion(core.ModuleBoot)
Обратите внимание, что после имени функции нет скобок: вы передаете саму функцию как параметр, не вызывая функцию и передавая ее возвращаемое значение.