如何使用反射获取任意方法的签名?

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

How to get an arbitrary method signature using reflection?

问题

我可以帮你解决这个挑战,这里是playground

最终目标是将函数和结构体的公共函数注册到活动管理器中,并通过函数名执行它们,大致如下所示:

   pool := map[string]interface{}{
       "Sample": func(ctx context.Context) error,
       "Sample2": func(ctx context.Context, args ...interface{}) error,
       "SampleFromStruct": func(ctx context.Context) error,
       "Sample2FromStruct": func(ctx context.Context, args ...interface{}) error,
   }

这些函数看起来像这样:

func Sample(ctx context.Context) error {
	fmt.Println("exec Sample")
	return nil
}

func Sample2(ctx context.Context, args interface{}) error {
	arguments := struct {
		Foo string `json:"foo"`
		Bar string `json:"bar"`
	}{}

	b, err := json.Marshal(args)
	if err != nil {
		return err
	}

	if err := json.Unmarshal(b, &arguments); err != nil {
		return err
	}

	fmt.Println("exec Sample2 with args", arguments)

	return nil
}

// 以及相同的结构体
type ActivityInStruct struct {
	Bar string
}

func (a *ActivityInStruct) SampleInStruct(ctx context.Context) error {
	fmt.Println("Value of Bar", a.Bar)
	return Sample(ctx)
}

func (a *ActivityInStruct) Sample2InStruct(ctx context.Context, args interface{}) error {
	fmt.Println("Value of Bar", a.Bar)
	return Sample2(ctx, args)
}

说到这里,我已经用以下实现使函数正常工作:

type activityManager struct {
	fnStorage map[string]interface{}
}

func (lm *activityManager) Register(fn interface{}) error {
	fnName := strings.Split((runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()), ".")
	name := fnName[len(fnName)-1]
	lm.fnStorage[name] = fn
	return nil
}

func (lm *activityManager) Exec(ctx context.Context, fnName string, args ...interface{}) error {
	fn, ok := lm.fnStorage[fnName]
	if !ok {
		return fmt.Errorf("activity %s not found", fnName)
	}

	if signatureCtx, ok := fn.(func(context.Context) error); ok {
		return signatureCtx(ctx)
	}

	if signatureWithArgument, ok := fn.(func(context.Context, interface{}) error); ok {
		return signatureWithArgument(ctx, args[0])
	}

	return fmt.Errorf("signature for %s not supported", fnName)

}

因此,执行过程如下:


func NewManager() *activityManager {
	return &activityManager{
		fnStorage: map[string]interface{}{},
	}
}

/*...*/
    ctx := context.Background()
    manager := NewManager()
	manager.Register(Sample)
	manager.Register(Sample2)

    if err := manager.Exec(ctx, "Sample"); err != nil {
		fmt.Println("Sample error", err.Error())
		return
	}

	args1 := map[string]interface{}{
		"foo": "isFoo",
		"bar": "isBar",
	}
	if err := manager.Exec(ctx, "Sample2", args1); err != nil {
		fmt.Println("Sample2 error", err.Error())
		return
	}

然而,要注册这样的内容:


func (lm *activityManager) RegisterStruct(fn interface{}) error {
	t := reflect.TypeOf(fn)
	for i := 0; i < t.NumMethod(); i++ {
		m := t.Method(i)
		if m.IsExported() {

			/*
							   This won't work cause m.Type are
							   func(*main.ActivityInStruct, context.Context, interface {}) error
				                           func(*main.ActivityInStruct, context.Context) error

				                          instead of
				                          func(context.Context, interface {}) error
				                          func(context.Context) error
			*/

			lm.fnStorage[m.Name] = m.Func
		}
	}

	return nil
}



/* Register Activity from Public methods in struct */
	activitiesStruct := &ActivityInStruct{
		Bar: "I'm the Bar",
	}

	manager.RegisterStruct(activitiesStruct)

我无法使其正常工作,因为反射显示的方法签名是这样的:func(*main.ActivityInStruct, context.Context, interface {}) error

有什么办法可以解决这个问题吗?完整的playground在这里

英文:

I can use an extra pair of eyes solving this challenge, playground here

Ultimate goal is to register functions and struct public functions into an activity manager and execute them via function name, so something along the lines of:

   pool := map[string]interface{
       &quot;Sample&quot;: func(ctx context.Context) error,
       &quot;Sample2&quot;: func(ctx context.Context, args ...interface{}) error,
       &quot;SampleFromStruct&quot;: func(ctx context.Context) error,
       &quot;Sample2FromStruct&quot;: func(ctx context.Context, args ...interface{}) error,
   }

the functions looks like:

func Sample(ctx context.Context) error {
	fmt.Println(&quot;exec Sample&quot;)
	return nil
}

func Sample2(ctx context.Context, args interface{}) error {
	arguments := struct {
		Foo string `json:&quot;foo&quot;`
		Bar string `json:&quot;bar&quot;`
	}{}

	b, err := json.Marshal(args)
	if err != nil {
		return err
	}

	if err := json.Unmarshal(b, &amp;arguments); err != nil {
		return err
	}

	fmt.Println(&quot;exec Sample2 with args&quot;, arguments)

	return nil
}

// and same but with struct
type ActivityInStruct struct {
	Bar string
}

func (a *ActivityInStruct) SampleInStruct(ctx context.Context) error {
	fmt.Println(&quot;Value of Bar&quot;, a.Bar)
	return Sample(ctx)
}

func (a *ActivityInStruct) Sample2InStruct(ctx context.Context, args interface{}) error {
	fmt.Println(&quot;Value of Bar&quot;, a.Bar)
	return Sample2(ctx, args)
}

Said this, I got it working with functions with the followed implementation:

type activityManager struct {
	fnStorage map[string]interface{}
}

func (lm *activityManager) Register(fn interface{}) error {
	fnName := strings.Split((runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()), &quot;.&quot;)
	name := fnName[len(fnName)-1]
	lm.fnStorage[name] = fn
	return nil
}

func (lm *activityManager) Exec(ctx context.Context, fnName string, args ...interface{}) error {
	fn, ok := lm.fnStorage[fnName]
	if !ok {
		return fmt.Errorf(&quot;activity %s not found&quot;, fnName)
	}

	if signatureCtx, ok := fn.(func(context.Context) error); ok {
		return signatureCtx(ctx)
	}

	if signatureWithArgument, ok := fn.(func(context.Context, interface{}) error); ok {
		return signatureWithArgument(ctx, args[0])
	}

	return fmt.Errorf(&quot;signature for %s not supported&quot;, fnName)

}

so the Execution looks like this:


func NewManager() *activityManager {
	return &amp;activityManager{
		fnStorage: map[string]interface{}{},
	}
}

/*...*/
    ctx := context.Background()
    manager := NewManager()
	manager.Register(Sample)
	manager.Register(Sample2)

    if err := manager.Exec(ctx, &quot;Sample&quot;); err != nil {
		fmt.Println(&quot;Sample error&quot;, err.Error())
		return
	}

	args1 := map[string]interface{}{
		&quot;foo&quot;: &quot;isFoo&quot;,
		&quot;bar&quot;: &quot;isBar&quot;,
	}
	if err := manager.Exec(ctx, &quot;Sample2&quot;, args1); err != nil {
		fmt.Println(&quot;Sample2 error&quot;, err.Error())
		return
	}

However, to register something like this:


func (lm *activityManager) RegisterStruct(fn interface{}) error {
	t := reflect.TypeOf(fn)
	for i := 0; i &lt; t.NumMethod(); i++ {
		m := t.Method(i)
		if m.IsExported() {

			/*
							   This won&#39;t work cause m.Type are
							   func(*main.ActivityInStruct, context.Context, interface {}) error
				                           func(*main.ActivityInStruct, context.Context) error

				                          instead of
				                          func(context.Context, interface {}) error
				                          func(context.Context) error
			*/

			lm.fnStorage[m.Name] = m.Func
		}
	}

	return nil
}



/* Register Activity from Public methods in struct */
	activitiesStruct := &amp;ActivityInStruct{
		Bar: &quot;I&#39;m the Bar&quot;,
	}

	manager.RegisterStruct(activitiesStruct)

I cant get this to work cause the reflection shows the method signature like this instead, func(*main.ActivityInStruct, context.Context, interface {}) error

Any idea how to go around that? the full playground is here

答案1

得分: 0

调用Value.Method来获取方法值

func (lm *activityManager) RegisterStruct(fn interface{}) error {
    v := reflect.ValueOf(fn)
    t := v.Type()
    for i := 0; i < t.NumMethod(); i++ {
        m := t.Method(i)
        if m.IsExported() {
            lm.fnStorage[m.Name] = v.Method(i).Interface()
        }
    }
    return nil
}
英文:

Call Value.Method to get the method value.

func (lm *activityManager) RegisterStruct(fn interface{}) error {
	v := reflect.ValueOf(fn)
	t := v.Type()
	for i := 0; i &lt; t.NumMethod(); i++ {
		m := t.Method(i)
		if m.IsExported() {
			lm.fnStorage[m.Name] = v.Method(i).Interface()
		}
	}
	return nil
}

huangapple
  • 本文由 发表于 2022年9月15日 01:11:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/73720778.html
匿名

发表评论

匿名网友

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

确定