【问题标题】:get reflect.struct from interface从接口获取 reflect.struct
【发布时间】:2021-11-18 11:40:27
【问题描述】:

你有这个函数来获取值的类型,但是我尝试了这个并且永远不能得到 reflect.struct:

type Test struct {
    Code int   
    Name string
}
func main(){
    test := getTest()
    data, err := getBytes(slice...)
    sanitizedFile := bytes.Split(data, []byte("\r\n"))
    err = Unmarshal(sanitizedFile[0], &test)
}
func getTest() interface{} {
    return Test{}
}

使用此代码,我无法从 Unmarshall func 中的 v 参数获取reflect.struct

func Unmarshal(data []byte, v interface{}) error {
    rv := reflect.ValueOf(v)

    if rv.Kind() == reflect.Ptr {
        rvElem := rv.Elem()
        
        switch rvElem.Kind() {
        case reflect.Struct:
           // implement me
        }
    }
    return ErrInvalid
}

我想知道我是否能以某种方式找出接口是否属于结构类型或访问该结构的值。

【问题讨论】:

  • 您正在传递一个指向接口 (*interface{}) 的指针,因此,我相信您需要 两个 Elem 调用。
  • 请注意*interface{}{struct{...}}interface{}{*struct{...}} 不一样,实际上完全不同。使用指向接口的指针而不是指向结构的指针可能会破坏您的解组实现。
  • 如果给定的输入可以是指针(甚至多个)和接口的混合,则可以使用循环调用Elemplay.golang.org/p/a4h1gBR2Xri

标签: go struct reflection


【解决方案1】:

我认为这里的真正问题可以通过这句话来说明:

我想知道我是否能以某种方式找出接口是否属于结构类型或访问该结构的值。

接口值不是“结构类型”。 从不!接口值可以包含一个类型为某种结构的值,但它不是该类型的值。它只是包含一个。这类似于您从亚马逊获得的盒子1 可以包含 一个开瓶器,但盒子永远不是 一个开瓶器。

对于某些接口类型I,给定一个类型为interface I 的非零值,您知道您有一个实现I 方法的值。由于{} 是空的方法集,所有类型都实现它,所以给定一个interface{} 类型的(仍然非零)值,你有一个不实现任何方法的值。这本身一点用处都没有:这意味着你不能调用任何方法,这意味着你不能做任何类似方法的事情。

但是仅仅因为你不能做任何事情-y并不意味着你不能做任何事情根本。任何接口值,无论接口类型如何,都可以使用类型断言:

iv := somethingThatReturnsAnInterface()
cv := iv.(struct S) // assert that iv contains a `struct S`

如果iv 确实包含struct S 值——如果这就是你打开盒子里面的东西——那么这个类型断言不会恐慌,cv加上struct S 类型的具体值。如果不希望出现恐慌,我们可以使用cv, ok := iv.(struct S) 表单或类型开关。所有这些——包括恐慌的版本——通过检查接口内的值的类型来工作

这个——或者更准确地说,Go 语言的定义方式——告诉我们interface“盒子”确实包含两件事:

  • 具体类型,并且
  • 一个具体的值。

嗯,也就是说,除非它持有 对,在这种情况下,iv == nil 为真。请注意,iv == nil 测试实际上测试了两个部分

如果 Go 有这样的语法,我们可以编写类似 iv.typeiv.value 的东西来处理这两个独立的部分。但我们不能那样做。我们必须使用类型断言、类型切换或reflect。所以,回到这个:

我想知道我是否能以某种方式找出接口是否属于结构类型

我们可以看到问题本身只是有点畸形。我们不想知道接口值是否具有这种类型。我们想知道一个非 nil 接口的 hold value 是否是 of 这种类型,就好像我们可以直接检查 iv.typeiv.value 一样。

如果您有一组有限的可能类型,您可以使用 type-switch 构造,并枚举所有允许的可能性:

switch cv := iv.(type) {
case struct S:
    // work with cv, which is a struct S
case *struct S:
    // work with cv, which is a *struct S
// add more cases as appropriate
}

如果您需要更多通用性,我们最终会使用 reflect 包,而不是执行上述操作:

tv := reflect.TypeOf(iv)

或:

vv := reflect.ValueOf(iv)

后者实际上是更有用的形式,因为vv 捕获了iv.type 伪字段iv.value 伪字段。

作为mkopriva notes in a commenttest,在您的示例代码中,类型为interface{},因此&test 的类型为*interface{}。在大多数情况下,这不是一个好主意:您只想直接传递 interface{} 值。

为了允许被调用的函数设置对象到一个新值,你需要传递一个指向对象的指针作为接口值。您不想将指针传递给接口,同时让接口将结构保持在“盒子中”。你需要一个reflect.Value,你可以在上面调用Set(),要得到一个,你需要在reflect.Value上跟随一个elem,它是一个指向结构的指针(不是一个指向结构的指针)界面)。

还有一个更完整的例子here on the Go Playground


1这部分是在暗示某些其他编程语言中的“装箱值”(参见What is boxing and unboxing and what are the trade offs?),但部分是字面意思。不过,不要将 Go 的接口误认为是 Java 的装箱值:它们根本不一样。

【讨论】:

    【解决方案2】:

    也许你需要的是类型断言?

    t,好的 := v.(myStruct)

    https://tour.golang.org/methods/15

    无论如何,这段代码都会打印“struct”:

        type tt struct {}
    
        var x tt
        var z interface{}
        z = x
        v := reflect.ValueOf(z).Kind()
        fmt.Printf("%v\n", v)
    

    使用反射设置结构字段的值,请参见:

    Using reflect, how do you set the value of a struct field?

    【讨论】:

      猜你喜欢
      • 2012-07-10
      • 2012-02-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-29
      • 2011-09-08
      相关资源
      最近更新 更多