通过名称在Go中调用一个结构体及其方法?

huangapple go评论93阅读模式
英文:

Call a Struct and its Method by name in Go?

问题

我在这里找到了一个函数调用MethodByName()的链接http://golang.org/pkg/reflect/#Value.MethodByName,但这不完全是我想要的!(可能是因为我不知道如何使用它...我找不到任何使用它的示例)。我想要的是:

type MyStruct struct {
//这里是一些字段
} 
func (p *MyStruct) MyMethod { 
    println("我的语句。"); 
} 

CallFunc("MyStruct", "MyMethod"); 
//打印出"我的语句。"

所以我猜,首先我需要类似StructByName()的东西,然后再用它来调用MethodByName(),对吗?

英文:

I have found a function call MethodByName() here http://golang.org/pkg/reflect/#Value.MethodByName but it's not exactly what I want! (maybe because I don't know how to use it ... I cannot find any example with it). What I want is:

type MyStruct struct {
//some feilds here
} 
func (p *MyStruct) MyMethod { 
    println("My statement."); 
} 

CallFunc("MyStruct", "MyMethod"); 
//print out "My statement." 

So I guess, first I need something like StructByName() and after that use it for MethodByName(), is that right!?

答案1

得分: 71

要在对象上调用一个方法,首先使用<code>reflect.ValueOf</code>。然后通过名称找到方法,最后调用找到的方法。例如:

package main

import "fmt"
import "reflect"

type T struct {}

func (t *T) Foo() {
    fmt.Println("foo")
}

func main() {
    var t T
    reflect.ValueOf(&t).MethodByName("Foo").Call([]reflect.Value{})
}
英文:

To call a method on an object, first use <code>reflect.ValueOf</code>. Then find the method by name, and then finally call the found method. For example:

package main

import &quot;fmt&quot;
import &quot;reflect&quot;

type T struct {}

func (t *T) Foo() {
    fmt.Println(&quot;foo&quot;)
}

func main() {
    var t T
    reflect.ValueOf(&amp;t).MethodByName(&quot;Foo&quot;).Call([]reflect.Value{})
}

答案2

得分: 36

type YourT1 struct {}
func (y YourT1) MethodBar() {
//do something
}

type YourT2 struct {}
func (y YourT2) MethodFoo(i int, oo string) {
//do something
}

func Invoke(any interface{}, name string, args... interface{}) {
inputs := make([]reflect.Value, len(args))
for i, _ := range args {
inputs[i] = reflect.ValueOf(args[i])
}
reflect.ValueOf(any).MethodByName(name).Call(inputs)
}

func main() {
Invoke(YourT2{}, "MethodFoo", 10, "abc")
Invoke(YourT1{}, "MethodBar")
}

英文:
type YourT1 struct {}
func (y YourT1) MethodBar() {
    //do something
}

type YourT2 struct {}
func (y YourT2) MethodFoo(i int, oo string) {
    //do something
}

func Invoke(any interface{}, name string, args... interface{}) {
    inputs := make([]reflect.Value, len(args))
    for i, _ := range args {
        inputs[i] = reflect.ValueOf(args[i])
    }
    reflect.ValueOf(any).MethodByName(name).Call(inputs)
}

func main() {
     Invoke(YourT2{}, &quot;MethodFoo&quot;, 10, &quot;abc&quot;)
     Invoke(YourT1{}, &quot;MethodBar&quot;)
}

Really the code needs to check the method's input number and even whether the method itself exists.

You can reference this http://gowalker.org/reflect#Type

  1. Check "any" is a struct type
  2. Check "any" has "name" method
  3. Check the number of method "name" input parameters is equal the length of args
  4. Implement ret by reflect.Value.Interface()

and be careful the Ptr type;

Or you can use SomeInterface{} instead of directly using interface{} to ensure this "any" type, like this:

type Shape interface {
    Area() float64  //some method to ensure any is an Shape type.
}

func Invoke(s Shape, name string, inputs...interface{}) []interface{} {
}

so this is OK

color := Invoke(Circle{}, &quot;GetColor&quot;)[0].(Color)

but

Invoke(NotAShape{}, &quot;ForBar&quot;)

can't be compiled because NotAnShape isn't a Shape.

If you can't be sure about the first type at compile time, you can build a map to store all possible type, like this:

map[string]reflect.Value{
    &quot;YourT1&quot; : reflect.ValueOf(YourT1{})
    &quot;YourT2&quot; : reflect.ValueOf(YourT2{})
    &quot;Circle&quot; : reflect.ValueOf(Cirlce{}) // or reflect.ValueOf(&amp;Circle{})
}  

答案3

得分: 3

// 调用带有错误处理的结构体方法

// Invoke - firstResult, err := Invoke(AnyStructInterface, MethodName, Params...)
func invoke(any interface{}, name string, args ...interface{}) (reflect.Value, error) {
method := reflect.ValueOf(any).MethodByName(name)
methodType := method.Type()
numIn := methodType.NumIn()
if numIn > len(args) {
return reflect.ValueOf(nil), fmt.Errorf("方法 %s 必须至少有 %d 个参数。当前有 %d 个", name, numIn, len(args))
}
if numIn != len(args) && !methodType.IsVariadic() {
return reflect.ValueOf(nil), fmt.Errorf("方法 %s 必须有 %d 个参数。当前有 %d 个", name, numIn, len(args))
}
in := make([]reflect.Value, len(args))
for i := 0; i < len(args); i++ {
var inType reflect.Type
if methodType.IsVariadic() && i >= numIn-1 {
inType = methodType.In(numIn - 1).Elem()
} else {
inType = methodType.In(i)
}
argValue := reflect.ValueOf(args[i])
if !argValue.IsValid() {
return reflect.ValueOf(nil), fmt.Errorf("方法 %s。参数[%d] 必须是 %s 类型。当前是 %s", name, i, inType, argValue.String())
}
argType := argValue.Type()
if argType.ConvertibleTo(inType) {
in[i] = argValue.Convert(inType)
} else {
return reflect.ValueOf(nil), fmt.Errorf("方法 %s。参数[%d] 必须是 %s 类型。当前是 %s", name, i, inType, argType)
}
}
return method.Call(in)[0], nil
}

英文:

gist
invoke struct method with error handling

// Invoke - firstResult, err := Invoke(AnyStructInterface, MethodName, Params...)
func invoke(any interface{}, name string, args ...interface{}) (reflect.Value, error) {
	method := reflect.ValueOf(any).MethodByName(name)
	methodType := method.Type()
	numIn := methodType.NumIn()
	if numIn &gt; len(args) {
		return reflect.ValueOf(nil), fmt.Errorf(&quot;Method %s must have minimum %d params. Have %d&quot;, name, numIn, len(args))
	}
	if numIn != len(args) &amp;&amp; !methodType.IsVariadic() {
		return reflect.ValueOf(nil), fmt.Errorf(&quot;Method %s must have %d params. Have %d&quot;, name, numIn, len(args))
	}
	in := make([]reflect.Value, len(args))
	for i := 0; i &lt; len(args); i++ {
		var inType reflect.Type
		if methodType.IsVariadic() &amp;&amp; i &gt;= numIn-1 {
			inType = methodType.In(numIn - 1).Elem()
		} else {
			inType = methodType.In(i)
		}
		argValue := reflect.ValueOf(args[i])
		if !argValue.IsValid() {
			return reflect.ValueOf(nil), fmt.Errorf(&quot;Method %s. Param[%d] must be %s. Have %s&quot;, name, i, inType, argValue.String())
		}
		argType := argValue.Type()
		if argType.ConvertibleTo(inType) {
			in[i] = argValue.Convert(inType)
		} else {
			return reflect.ValueOf(nil), fmt.Errorf(&quot;Method %s. Param[%d] must be %s. Have %s&quot;, name, i, inType, argType)
		}
	}
	return method.Call(in)[0], nil
}

答案4

得分: 0

package main

import (
"fmt"
"reflect"
)

type Log struct {
Path string
Level string
}

func (l *Log) Conversion(i interface{}) {

if data, ok := i.(*Log); ok {
    if data != nil {
        if len(data.Path) > 0 {
            l.Path = data.Path
        }
        if len(data.Level) > 0 {
            l.Level = data.Level
        }
    }
}

}

type Storage struct {
Type string
ServerList []string
}

func (s *Storage) Conversion(i interface{}) {

if data, ok := i.(*Storage); ok {
if data != nil {
if len(data.Type) > 0 {
s.Type = data.Type
}
}
}
}

type Server struct {
LogConfig *Log
StorageConfig *Storage
}

func main() {
def := Server{
LogConfig: &Log{
Path: "/your/old/log/path/",
Level: "info",
},
StorageConfig: &Storage{
Type: "zookeeper",
ServerList: []string{"127.0.0.1:2181"},
},
}

fmt.Println(def)
cur := Server{
    LogConfig: &Log{
        Path:  "/your/new/log/path/",
        Level: "debug",
    },
    StorageConfig: &Storage{
        Type:       "etcd",
        ServerList: []string{"127.0.0.1:2379"},
    },
}

fmt.Println(cur)

defV := reflect.ValueOf(def)
curV := reflect.ValueOf(cur)
for k := 0; k < defV.NumField(); k++ {
    in := make([]reflect.Value, 1)
    in[0] = reflect.ValueOf(curV.Field(k).Interface())
    defV.Field(k).MethodByName("Conversion").Call(in)
}

fmt.Println(def.LogConfig)
fmt.Println(def.StorageConfig)

}

英文:
package main

import (
    &quot;fmt&quot;
    &quot;reflect&quot;
)

type Log struct {
    Path  string
    Level string
}

func (l *Log) Conversion(i interface{}) {

    if data, ok := i.(*Log); ok {
        if data != nil {
            if len(data.Path) &gt; 0 {
                l.Path = data.Path
            }
            if len(data.Level) &gt; 0 {
                l.Level = data.Level
            }
        }
    }
}

type Storage struct {
    Type       string
    ServerList []string
}

func (s *Storage) Conversion(i interface{}) {

   if data, ok := i.(*Storage); ok {
        if data != nil {
            if len(data.Type) &gt; 0 {
                s.Type = data.Type
            }
        }
    }
}

type Server struct {
    LogConfig     *Log
    StorageConfig *Storage
}

func main() {
    def := Server{
        LogConfig: &amp;Log{
            Path:  &quot;/your/old/log/path/&quot;,
            Level: &quot;info&quot;,
        },
        StorageConfig: &amp;Storage{
            Type:       &quot;zookeeper&quot;,
            ServerList: []string{&quot;127.0.0.1:2181&quot;},
        },
    }

    fmt.Println(def)
    cur := Server{
        LogConfig: &amp;Log{
            Path:  &quot;/your/new/log/path/&quot;,
            Level: &quot;debug&quot;,
        },
        StorageConfig: &amp;Storage{
            Type:       &quot;etcd&quot;,
            ServerList: []string{&quot;127.0.0.1:2379&quot;},
        },
    }

    fmt.Println(cur)

    defV := reflect.ValueOf(def)
    curV := reflect.ValueOf(cur)
    for k := 0; k &lt; defV.NumField(); k++ {
        in := make([]reflect.Value, 1)
        in[0] = reflect.ValueOf(curV.Field(k).Interface())
        defV.Field(k).MethodByName(&quot;Conversion&quot;).Call(in)
    }

    fmt.Println(def.LogConfig)
    fmt.Println(def.StorageConfig)
}

答案5

得分: 0

package main
import ("fmt";"math/rand";"reflect";"time")

func Invoke(obj any, name string, args ...any) []reflect.Value {
    inputs := make([]reflect.Value, len(args))
    for i, _ := range args {
        inputs[i] = reflect.ValueOf(args[i])
    }
    return reflect.ValueOf(obj).MethodByName(name).Call(inputs)
}

type Score struct{}

func (s Score) IsExcellent(score int) (bool, error) {
    if score < 0 {
        return false, fmt.Errorf("invalid score")
    }
    if score > 90 {
        return true, nil
    }
    return false, nil
}

func (s Score) Generate(min, max int) (int, time.Time) {
    rand.Seed(time.Now().UnixNano())
    return rand.Intn(max-min) + min, time.Now()
}

func main() {
    s := Score{}
    values1 := Invoke(s, "IsExcellent", 95)
    values2 := Invoke(s, "IsExcellent", -5)
    for _, values := range [][]reflect.Value{values1, values2} {
        if len(values) > 0 {
            err := values[1].Interface()
            if err != nil {
                fmt.Println(err.(error))
                continue
            }
            fmt.Println(values[0].Bool())
        }
    }
    values := Invoke(Score{}, "Generate", 0, 101)
    randNumber := values[0].Int()
    createTime := values[1].Interface().(time.Time) // It's better to check `values[1].Interface()` equal to nil first before you do assertion.
    fmt.Println(randNumber, createTime)
}

<kbd>go playground</kbd>

英文:

This is great. I added the return value example for it.

package main
import (&quot;fmt&quot;;&quot;math/rand&quot;;&quot;reflect&quot;;&quot;time&quot;)

func Invoke(obj any, name string, args ...any) []reflect.Value {
	inputs := make([]reflect.Value, len(args))
	for i, _ := range args {
		inputs[i] = reflect.ValueOf(args[i])
	}
	return reflect.ValueOf(obj).MethodByName(name).Call(inputs)
}

type Score struct{}

func (s Score) IsExcellent(score int) (bool, error) {
	if score &lt; 0 {
		return false, fmt.Errorf(&quot;invalid score&quot;)
	}
	if score &gt; 90 {
		return true, nil
	}
	return false, nil
}

func (s Score) Generate(min, max int) (int, time.Time) {
	rand.Seed(time.Now().UnixNano())
	return rand.Intn(max-min) + min, time.Now()
}

func main() {
	s := Score{}
	values1 := Invoke(s, &quot;IsExcellent&quot;, 95)
	values2 := Invoke(s, &quot;IsExcellent&quot;, -5)
	for _, values := range [][]reflect.Value{values1, values2} {
		if len(values) &gt; 0 {
			err := values[1].Interface()
			if err != nil {
				fmt.Println(err.(error))
				continue
			}
			fmt.Println(values[0].Bool())
		}
	}
	values := Invoke(Score{}, &quot;Generate&quot;, 0, 101)
	randNumber := values[0].Int()
	createTime := values[1].Interface().(time.Time) // It&#39;s better to check `values[1].Interface()` equal to nil first before you do assertion.
	fmt.Println(randNumber, createTime)
}

<kbd>go playground</kbd>

huangapple
  • 本文由 发表于 2011年11月12日 17:26:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/8103617.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定