【问题标题】:How to instantiate value of unknown type in Go?如何在 Go 中实例化未知类型的值?
【发布时间】:2017-01-19 04:01:15
【问题描述】:

我用 golang 开发了一些服务器。我尝试创建一些包装函数,这可以在将来帮助我。

我有什么:

1) 我有一些 DTO 结构,例如:

type Request struct {
    Field1   string `json:"field1"`
    Field2   string `json:"field2"`
}

type Response struct {
    Field1   string `json:"field1"`
    Field2   string `json:"field2"`
    Field3   string `json:"field3"`
}

2) 我在控制器层有一些函数,它们(按照约定)接收 1 个参数(指向结构的指针)并返回 1 个结果(指向结构的指针),例如:

func SomeHandler(request *Request) *Response{
    ...do something
    return &Response{"first","second","third"}
}

我需要什么:

我需要编写作为参数接收的包装函数:

  1. 指向“控制器”函数的指针
  2. http.ResponseWriter
  3. *http.Request

这个包装函数必须:

  1. 确定“控制器”函数的参数类型
  2. 确定“控制器”函数的结果类型
  3. 从 *http.Request 的正文中实例化并填充参数值(从 json 解码)
  4. 使用上一步参数实例化的调用控制器函数
  5. 将上一步的结果写入http.ResponseWriter(编码为json)

包装器必须与任何类型的“控制器”函数一起正常工作 - 此函数的签名不同(不同的参数类型,不同的结果类型)

谁能帮我实现这个包装器?

【问题讨论】:

    标签: go reflection


    【解决方案1】:

    你所做的有点奇怪,但reflect 可以提供你需要的所有信息。

    func myFunction(a string, b int32) error {
        return nil
    }
    
    func myWrapper(mystery interface{}) {
        typ := reflect.TypeOf(mystery)
    
        // Check typ.Kind before playing with In(i);
        for i := 0; i < typ.NumIn(); i++ {
            fmt.Printf("Param %d: %v\n", i, typ.In(i))
        }
        for i := 0; i < typ.NumOut(); i++ {
            fmt.Printf("Result %d: %v\n", i, typ.Out(i))
        }
    
    }
    

    打印出来:

    Param 0: string
    Param 1: int32
    Result 0: error
    

    【讨论】:

    • 感谢您的回答。你写道我做的事情有点连线,也许你是对的,我是 golang 的新手。那么解决问题的方法是什么?我不想多次编写类似的代码,但是在很多“控制器”函数中我需要做同样的事情:将输入从 json 解码到我的模型(结构),将数据从我的对象编码到 json 并编写它输出,但我不明白怎么做,你能帮我吗?
    • @YuriiVasilchuk 我不了解您的确切用例。我认为你可以改变你的方法,使用一个函数来进行编码和解码(go 的 Marshal/Unmarshal 非常通用:它们采用接口{},因此你可以传递任何东西)。所有这个函数需要知道的是源/目标类型,我认为你从处理程序“知道”。
    【解决方案2】:

    这样的东西应该可以工作(未经测试):

    func wrapper(ctlr interface{}, w http.ResponseWriter, r *http.Request) error {
    
        tpe := reflect.TypeOf(ctlr)
        if tpe.Kind() != reflect.Func || tpe.NumIn() != 1 || tpe.NumOut() != 1 {
            // TODO: handle wrong ctlr type
        }
    
        // 1. Determine type of argument of 'controller' function
        argt := tpe.In(0)
    
        // 2. Determine type of result of 'controller' function
        // rest := tpe.Out(0) // commented out since it's useless
    
        // 3. Instantiate and fill argument value from Body of *http.Request (decode from json)
        arg := reflect.Zero(argt)
        err := json.NewDecoder(r.Body).Decode(&arg)
        if err != nil {
            // TODO: handle err
        }
    
        // 4. Call controller Function with instantiated on previous step argument
        resv := reflect.ValueOf(ctlr).Call([]reflect.Value{arg})[0]
    
        // 5. Write results of previous step into http.ResponseWriter (encoded as json)
        err = json.NewEncoder(w).Encode(resv.Interface())
        if err != nil {
            // TODO: handle err
        }
    
        return nil
    }
    

    注意,第二步是不必要的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-08
      • 1970-01-01
      • 1970-01-01
      • 2015-11-15
      • 2017-12-27
      • 2010-12-15
      • 1970-01-01
      相关资源
      最近更新 更多