【问题标题】:Can a variable be used as a placeholder for a function call?变量可以用作函数调用的占位符吗?
【发布时间】:2019-11-21 00:01:25
【问题描述】:

我正在编写一个程序,它有几个结构和函数来不同地处理这些结构。我有一个通用函数,它根据输入调用所需的函数。有没有一种通用的方法来使用 getStruct() 的返回值?

package main

var X func(s []string) A
var Y func(s []string) B

type A struct {
    Name  string
    Place string
}

type B struct {
    Name  string
    Place string
    Value string
}

func newA(s []string) A {
    a := A{
        Name:  s[0],
        Place: s[1],
    }
    return a
}

func newB(s []string) B {
    a := B{
        Name:  s[0],
        Place: s[1],
        Value: s[2],
    }
    return a
}

func getStruct(t string) interface{} {

    switch {
    case t == "A":
        return X
    case t == "B":
        return Y
    default:
        return //someStruct
    }

}

func main() {

    buildNewStruct := getStruct("A") //Lets assume "A" here is got as an argument

    var strSlice = []string{"Bob", "US"}
    buildNewStruct(strSlice) //How to do this operation? 
                             //I am hoping to use buildNewStruct(strSlice) to dynamically call 
                             //either of newA(strSlice) or newB(strSlice) function

}

我曾尝试查看thisthis,后者与我的问题不完全相同。

由于我是新手,我不确定这样的事情是否可行。

【问题讨论】:

  • 查看此答案的第 2 部分:stackoverflow.com/a/48492325/141555。您将执行与 getStruct 基本相同的操作,但不是使用 t 来确定结构,而是使用 interface{} 中的信息,即底层 type
  • 如果您的要求是,给定 len k 的切片 s,将其值应用于给定结构 S 的等效索引定位字段,那么我将使用反射。
  • @sberry 对,根据@burak,我仍然需要根据我的逻辑验证type。我认为没有其他方法可以解决这个问题。感谢您的帮助。
  • @mh-cbon 问题是,我有许多不同的结构,具有相同数量的字段
  • @sberry,对不起,我现在明白你所说的type 是什么意思了。我的错!我想我现在可以做我想做的事了。谢谢!

标签: go


【解决方案1】:

您可以使用reflect 包将结构属性设置为来自[]interface{} 切片的等效索引定位值。

package main

import (
    "fmt"
    "log"
    "reflect"
)

func main() {
    var a A
    err := decode(&a, []interface{}{"Name", "Place"})
    log.Println(err)
    log.Println(a)
}

func decode(dst interface{}, values []interface{}) error {
    rvptr := reflect.ValueOf(dst)
    if rvptr.Kind() != reflect.Ptr {
        return fmt.Errorf("value must be ptr")
    }
    rv := rvptr.Elem()
    if rv.NumField() < len(values) {
        return fmt.Errorf("too many values")
    }
    if rv.NumField() > len(values) {
        return fmt.Errorf("too few values")
    }
    rvalues := reflect.ValueOf(values)
    for i := range values {
        f := rv.FieldByIndex([]int{i})
        f.Set(rvalues.Index(i).Elem())
    }
    return nil
}

type A struct {
    Name  string
    Place string
}

type B struct {
    Name  string
    Place string
    Value string
}

打印

$ go run main.go 
2019/11/21 17:00:17 <nil>
2019/11/21 17:00:17 {Name Place}

【讨论】:

    【解决方案2】:

    问题在于函数的返回类型。

    func newA(in []string) interface{} {...}
    func newB(in []string) interface{} {...}
    
    func getStruct(name string) func([]string) interface{} {
      switch name {
         case "A": return newA
         case "B": return newB
      }
      return nil
    }
    
    func main() {
      buildNewStruct := getStruct("A") 
      var strSlice = []string{"Bob", "US"}
      str:=buildNewStruct(strSlice)
      if a, ok:=str.(A); ok {
         ...
      }
    }
    

    使用这种方法,即使您通过调用统一的buildNewStruct() 保存了一些代码,您也必须使用类型断言来确定从该函数返回的内容,因此这可能没有多大意义。不过,这取决于您的具体用例。

    【讨论】:

    • 我同意。我希望找到一种更通用、更有效的方法来做到这一点。我现在将解决建议的方法。感谢您的帮助。
    猜你喜欢
    • 1970-01-01
    • 2014-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多