反射认为结构体Value也是一个指针吗?

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

Reflect thinks struct Value is also a ptr?

问题

我有一个类似于这个示例的数据结构。如你所见,foo 包含一个指向 bar 的嵌入指针:

type foo struct {
    *bar
}

type bar struct {
    S []byte
}

我正在使用 reflect 包,代码如下:

func test(x interface{}) {
    var v = reflect.ValueOf(x)

    if v.Kind() == reflect.Struct {
        fmt.Println("是一个结构体")

        // 报错:reflect: 在结构体 Value 上调用 reflect.Value.Elem
        // v = v.Elem()

        // 报错:reflect: 在指针 Value 上调用 reflect.Value.Field
        v = v.FieldByName("S")
    }
}

func main() {
    var f foo
    test(f)
    fmt.Println(string(f.S))
}

所以 v.Kind() 被识别为 reflect.Struct,但是如果我尝试使用 .FieldByName("S") 将其视为结构体处理,它会报错,因为它认为 v 是一个指针。

然后,如果我尝试使用 .Elem() 将其视为指针处理,它又会报错,因为它认为 v 是一个结构体。

我尝试过使用 reflect.Indirect(),以及其他一些方法,但是我无法找到如何获取嵌入指针的字段的方法。

有没有办法从嵌入指向结构体的指针中获取 reflect.Value 表示?

示例代码: http://play.golang.org/p/n0eea6XW3I


编辑: 我还尝试过 v = v.FieldByName("bar"),但是报错如下:

panic: 运行时错误:无效的内存地址或空指针解引用
英文:

I have a data structure like this demo. As you can see, foo has an embedded pointer to bar:

type foo struct {
	*bar
}

type bar struct {
	S []byte
}

And I'm using the reflect package like this:

func test(x interface{}) {

	var v = reflect.ValueOf(x)

	if v.Kind() == reflect.Struct {
		fmt.Println("was a struct")

	// panic: reflect: call of reflect.Value.Elem on struct Value
	//	v = v.Elem()

	// panic: reflect: call of reflect.Value.Field on ptr Value
		v = v.FieldByName("S")
	}
}

func main() {
	var f foo
	test(f)
	fmt.Println(string(f.S))
}

So v.Kind() is recognized as a reflect.Struct, but if I try to treat it like a struct by using .FieldByName("S"), it panics because it thinks v is a ptr.

So then if I try to treat it like a ptr by calling .Elem(), it panics because it thinks v is a struct.

I've tried reflect.Indirect(), as well as a few other things, but I can't figure out how to get the field of an embedded pointer.

Is there a way to get the reflect.Value representation from an embedded pointer to a struct?

Demo: http://play.golang.org/p/n0eea6XW3I


EDIT: Also tried v = v.FieldByName("bar"), but got:

><code>panic: runtime error: invalid memory address or nil pointer dereference</code>

答案1

得分: 8

我们需要意识到的第一件事是,行var f foo等同于f := foo{}。这将初始化内部字段bar(类型为*bar)为其零值...即nil。嵌入类型和反射的行为似乎是将嵌入类型的字段视为类型本身的字段。因此,当您请求v.FieldByName("S")时,它试图在f的成员bar中查找该字段,而bar为nil。

您正在尝试执行(*f.bar).S(在Go中,不需要显式指针解引用,但这样更能说明我的观点)。现在的问题是:如果您将其更改为v.FieldByName("bar"),为什么会出现错误?原因相同。

仔细查看堆栈跟踪,FieldByName行不再崩溃,崩溃的是fmt.Println(string(f.S))行。再次强调,语义上您正在执行(*f.bar).S。但是成员"bar"为nil,因此实际上您正在进行nil指针解引用。

您可以通过将var f foo更改为f := foo{&bar{}}来修复这两个错误。

英文:

The first thing we need to realize is that the line var f foo is equivalent to f := foo{}. This initializes the internal field bar (of type *bar) to its zero value... nil. The behavior of embedded types and reflect seems to be that it treats the embedded type's fields as fields of the type itself. So when you request v.FieldByName(&quot;S&quot;) it's trying to find that field in f's member, bar, which is nil.

You're trying to do this (*f.bar).S. (In Go the explicit pointer dereference isn't needed, but it makes my point). Now the question is: if you change is to v.FieldByName(&quot;bar&quot;) why does it give an error? Same reason.

Look closely at the stack trace, the FieldByName line no longer crashes, the line that crashes is fmt.Println(string(f.S)). Again, semantically you're doing (*f.bar).S. But the member "bar" is nil, so you are, in fact, doing a nil pointer dereference.

You can fix both errors by changing var f foo to f := foo{&amp;bar{}}.

答案2

得分: 1

我遇到了这个错误:"panic: reflect: call of reflect.Value.Elem on struct Value",原因是这行代码:"reflect.ValueOf(parameterName).Elem()"

  1. 当我在反射中使用Elem()时,意味着parameterName在valueOf()内部应该是一个指向结构体的指针。
func Search(flight interface{}, key string) string {

  val := reflect.ValueOf(flight).Elem()

  for i := 0; i < val.NumField(); i++ {
    valueField := val.Field(i)
    typeField := val.Type().Field(i)
    if key == strings.ToLower(typeField.Name) {
      return valueField.Interface().(string)
    }
  }
  return ""
}

现在,在调用search函数时,我的调用应该是这样的!
result := Search(&flights, key)

英文:

i was getting this error " panic: reflect: call of reflect.Value.Elem on struct Value" bcz of this line "reflect.ValueOf(parameterName).Elem()"

1.When i am using Elem() in reflex ,it means parameterName inside valueOf() should be a pointer to structure

func Search(flight interface{}, key string) string {

  val := reflect.ValueOf(flight).Elem()

  for i := 0; i &lt; val.NumField(); i++ {
    valueField := val.Field(i)
    typeField := val.Type().Field(i)
    if key == strings.ToLower(typeField.Name) {
      return valueField.Interface().(string)
    }
  }
  return &quot;&quot;
}

Now while calling search function my call should be like this!
result := Search(&flights, key)

huangapple
  • 本文由 发表于 2014年2月28日 11:15:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/22085485.html
匿名

发表评论

匿名网友

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

确定