从接口获取 reflect.struct

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

get reflect.struct from interface

问题

你好,我明白你的问题。你想知道如何判断一个接口是否是结构体类型,以及如何访问该结构体的值。

在Go语言中,可以使用反射(reflect)包来实现这个目的。你可以通过调用reflect.TypeOf函数来获取一个值的类型,并判断是否为结构体类型。具体代码如下:

func isStruct(v interface{}) bool {
    t := reflect.TypeOf(v)
    return t.Kind() == reflect.Struct
}

通过调用isStruct函数并传入一个接口值,你可以判断该接口是否为结构体类型。

要访问结构体的值,你可以使用reflect.ValueOf函数获取值的反射对象,然后使用反射对象的相关方法来获取结构体字段的值。以下是一个示例代码:

func printStructValues(v interface{}) {
    rv := reflect.ValueOf(v)
    
    if rv.Kind() == reflect.Struct {
        for i := 0; i < rv.NumField(); i++ {
            fieldValue := rv.Field(i)
            fieldName := rv.Type().Field(i).Name
            fmt.Printf("%s: %v\n", fieldName, fieldValue.Interface())
        }
    }
}

通过调用printStructValues函数并传入一个结构体值,你可以打印出结构体的字段名和对应的值。

希望这可以帮助到你!如果你有任何其他问题,请随时问我。

英文:

hi a have this func for get type of value, but i try this and never can get reflect.struct:

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

With this code i don't can get the reflecet.struct from v params in Unmarshall func

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
}

I would like to know if I can somehow find out if an interface is of type struct or access the values ​​of that struct.

答案1

得分: 2

我认为这里真正的问题可以通过以下引用来说明:

> 我想知道是否可以找出接口是否为结构体类型,或者访问该结构体的值。

接口值不是“结构体类型”。**绝对不是!**接口值可以包含一个类型为某个结构体的值,但它本身不是该类型的值。这类似于从亚马逊得到的盒子<sup>1</sup>可以包含一个螺丝刀,但盒子本身永远不是螺丝刀。

对于某个接口类型I,给定一个非nil的interface I类型的值,你知道你有一个实现了I方法的值。由于{}是空方法集,所有类型都实现了它,所以给定一个(仍然非nil的)interface{}类型的值,你有一个不实现任何方法的值。这本身并不是很有用:这意味着你无法调用任何方法,也就是说你不能执行任何类似方法的操作。

但是,仅仅因为你不能执行任何方法操作,并不意味着你不能执行任何操作。无论接口类型如何,都可以对接口值进行类型断言:

iv := somethingThatReturnsAnInterface()
cv := iv.(struct S) // 断言iv包含一个`struct S`

如果iv确实包含一个struct S值,也就是说当你打开盒子后里面的东西就是struct S,那么这个类型断言不会引发panic,并且cv将得到类型为struct S的具体值。如果不希望引发panic,我们可以使用cv, ok := iv.(struct S)形式,或者使用类型开关。所有这些(包括引发panic的版本)都是通过检查接口内部值的类型来工作的。

这个——更准确地说,Go语言的定义方式——告诉我们interface“盒子”实际上包含两个东西:

  • 一个具体类型,以及
  • 一个具体值。

嗯,也就是说,除非它包含一个&lt;nil, nil&gt;对,否则iv == nil为真。请注意,iv == nil测试实际上测试了两个部分

如果Go有一个这样的语法,我们可以写类似iv.typeiv.value来获取这两个独立的部分。但我们不能这样做。我们必须使用类型断言、类型开关或reflect。所以,回到这个问题:

> 我想知道是否可以找出接口是否为结构体类型

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

如果你有一组有限的可能类型,你可以使用类型开关结构,并列举所有允许的可能性:

switch cv := iv.(type) {
case struct S:
    // 使用cv,它是一个struct S
case *struct S:
    // 使用cv,它是一个*struct S
// 根据需要添加更多的case
}

如果你需要更一般化的方法,而不是上述的方法,我们将使用reflect包:

tv := reflect.TypeOf(iv)

或者:

vv := reflect.ValueOf(iv)

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

正如mkopriva在评论中指出的,在你的示例代码中,test的类型是interface{},所以&amp;test的类型是*interface{}。在大多数情况下,这不是一个好主意:你只想直接传递interface{}值。

如果允许被调用的函数将对象设置为一个新值,你将希望将对象的指针作为接口值传递。你不希望传递一个指向接口的指针,同时让接口持有结构体“在盒子里”。你需要一个reflect.Value,你可以在上面调用Set(),为了得到一个,你需要在指向结构体的指针上进行elem操作(而不是指向接口的指针)。

在Go Playground上有一个更完整的示例在这里


<sup>1</sup>这在一定程度上暗示了其他某些编程语言中的“封装值”(参见https://stackoverflow.com/q/13055/1256452),但部分是字面意义上的。不要把Go的接口误认为是Java的封装值,它们完全不同。

英文:

I think the real problem here is illustrated by this quote:

> I would like to know if I can somehow find out if an interface is of type struct or access the values ​​of that struct.

An interface value isn't "of type struct". Never! An interface value can contain a value whose type is some structure, but it is not a value of that type. It just contains one. This is similar to the way that a box<sup>1</sup> you get from Amazon can contain a corkscrew, but the box is not a corkscrew, ever.

Given a non-nil value of type interface I for some interface type I, you know that you have a value that implements the methods of I. Since {} is the empty set of methods, all types implement it, so given a (still non-nil) value of type interface{}, you have a value that implements no methods. That's not at all useful by itself: it means you can invoke no methods, which means you can't do anything method-like.

But just because you can't do anything method-y doesn't mean you can't do anything at all. Any interface value, regardless of the interface type, can have a type-assertion used on it:

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

If iv does in fact contain a struct S value—if that's what's inside the box once you open it—then this type-assertion doesn't panic, and cv winds up with the concrete value of type struct S. If panic is undesirable, we can use the cv, ok := iv.(struct S) form, or a type switch. All of these—including the version that panics—work by checking the type of the value inside the interface.

What this—or, more precisely, the way the Go language is defined—tells us is that the interface "box" really holds two things:

  • a concrete type, and
  • a concrete value.

Well, that is, unless it holds a &lt;nil, nil&gt; pair, in which case iv == nil is true. Note that the iv == nil test actually tests both parts.

If Go had a syntax for this, we could write something like iv.type and iv.value to get at the two separate parts. But we can't do that. We have to use type assertions, type-switch, or reflect. So, going back to this:

> I would like to know if I can somehow find out if an interface is of type struct

we can see that the question itself is just a little malformed. We don't want to know if an interface value has this type. We want to know if a non-nil interface's held value is of this type, as if we could inspect iv.type and iv.value directly.

If you have a limited set of possible types, you can use the type-switch construct, and enumerate all your allowed possiblities:

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
}

If you need more generality, instead of doing the above, we end up using the reflect package:

tv := reflect.TypeOf(iv)

or:

vv := reflect.ValueOf(iv)

The latter is actually the more useful form, since vv captures both the iv.type pseudo-field and the iv.value pseudo-field.

As mkopriva notes in a comment, test, in your sample code, has type interface{}, so &amp;test has type *interface{}. In most cases this is not a good idea: you just want to pass the interface{} value directly.

To allow the called function to set the object to a new value, you will want to pass a pointer to the object as the interface value. You do not want to pass a pointer to the interface while having the interface hold the struct "in the box" as it were. You need a reflect.Value on which you can invoke Set(), and to get one, you will need to follow an elem on the reflect.Value that is a pointer to the struct (not one that is a pointer to the interface).

There's a more complete example here on the Go Playground.


<sup>1</sup>This is partly an allusion to "boxed values" in certain other programming languages (see https://stackoverflow.com/q/13055/1256452), but partly literal. Don't mistake Go's interfaces for Java's boxed values, though: they are not the same at all.

答案2

得分: 1

也许你需要类型断言?

t, ok := 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)

关于使用反射设置结构字段的值,请参考以下链接:

https://stackoverflow.com/questions/6395076/using-reflect-how-do-you-set-the-value-of-a-struct-field

英文:

Maybe what you need is type assertion?

t, ok := v.(myStruct)

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

In any case this code prints "struct":

	type tt struct {}

	var x tt
	var z interface{}
	z = x
	v := reflect.ValueOf(z).Kind()
	fmt.Printf(&quot;%v\n&quot;, v)

And see this for setting the value of a struct field using reflection:

https://stackoverflow.com/questions/6395076/using-reflect-how-do-you-set-the-value-of-a-struct-field

huangapple
  • 本文由 发表于 2021年9月25日 23:42:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/69327690.html
匿名

发表评论

匿名网友

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

确定