在Golang中遇到了反射的困难。

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

Having a hard time with reflection in golang

问题

我正在尝试动态设计一个协议测试。

我需要使用的函数是go-ethereum的Decode
https://github.com/ethereum/go-ethereum/blob/master/p2p/message.go#L54

然后我的一些代码使用它:

  1. msg <- receive() //发送给我一个类型为p2p.Msg的消息
  2. var message MyTargetType
  3. msg.Decode(&message) //这个工作正常,这显然是使用函数的正确方式,使用变量的指针
  4. anotherMessage := output.Msg //这是一个interface{},见下面
  5. msg.Decode(&anotherMessage) //这个失败了

我不明白为什么Decode方法处理这两个不同。一个小测试程序:

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. type mystruct struct {
  7. hello string
  8. }
  9. func main() {
  10. var first mystruct
  11. second := mystruct{}
  12. fmt.Println(reflect.TypeOf(first))
  13. fmt.Println(reflect.TypeOf(second))
  14. }

这将打印相同的类型:

  1. main.mystruct
  2. main.mystruct

但是不知何故,上面的Decode,它在内部使用反射,却以不同的方式处理它们。

我的问题是什么?对于我的协议测试,我想定义预期的输出类型:

  1. type Output struct {
  2. Msg interface{}
  3. }

由于消息的类型可能非常不同,我认为唯一的方法是使用interface{}。因此:

  1. output := Output{
  2. Msg: MyTargetType{}.
  3. }
  4. //
  5. anotherOutput := Output{
  6. Msg: AnotherType{}.
  7. }
  8. //等等

这样,我稍后可以检查接收到的输出是否符合预期。但是,那个Decode方法让我发疯。

我尝试了几种使用反射的方法,例如:

  1. var decodedMsg = reflect.TypeOf(output.Msg)
  2. msg.Decode(&decodedMsg)

甚至是

  1. var decodedMsg = reflect.New(reflect.TypeOf(output.Msg)).Elem().Interface()
  2. fmt.Println(reflect.TypeOf(decodedMsg)) //这实际上打印出了正确的类型!!!
  3. // 但是Decode仍然失败,显示:
  4. // 给Decode的接口必须是指针
英文:

I am trying to dynamically design a protocol test.

The function which I need to use is go-ethereum's Decode:
https://github.com/ethereum/go-ethereum/blob/master/p2p/message.go#L54

Then some of my code uses it:

  1. msg &lt;- receive() //sends me a message of type p2p.Msg
  2. var message MyTargetType
  3. msg.Decode(&amp;message) // this works correctly and this is apparently the correct way to use the function, with a pointer to the variable
  4. anotherMessage := output.Msg // this is an interface{}, see below
  5. msg.Decode(&amp;anotherMessage) // this fails

I don't understand why the Decode method handles the two differently. A little test program:

  1. package main
  2. import (
  3. &quot;fmt&quot;
  4. &quot;reflect&quot;
  5. )
  6. type mystruct struct {
  7. hello string
  8. }
  9. func main() {
  10. var first mystruct
  11. second := mystruct{}
  12. fmt.Println(reflect.TypeOf(first))
  13. fmt.Println(reflect.TypeOf(second))
  14. }

This prints the types to be the same:

  1. main.mystruct
  2. main.mystruct

But somehow, Decode above, which uses reflection internally, handles them differently.

What's my problem? For my protocol test, I want to define the type of the output to be expected:

  1. type Output struct {
  2. Msg interface{}
  3. }

Since the messages can be of very different type, I thought the only way is to use interface{}. Therefore:

  1. output := Output{
  2. Msg: MyTargetType{}.
  3. }
  4. //
  5. anotherOutput := Output{
  6. Msg: AnotherType{}.
  7. }
  8. // and so on

so that I then later can check that the output received is the one expected. But that Decode method is driving me crazy.

I have tried several things with reflection, e.g.

  1. var decodedMsg = reflect.TypeOf(output.Msg)
  2. msg.Decode(&amp;decodedMsg)

or even

  1. var decodedMsg = reflect.New(reflect.TypeOf(output.Msg)).Elem().Interface()
  2. fmt.Println(reflect.TypeOf(decodedMsg)) //this actually prints the correct type!!!
  3. // But then Decode fails nonetheless with:
  4. // interface given to Decode must be a pointer

答案1

得分: 1

将Decode的参数改为指向目标类型的指针。使用以下代码:

  1. output := Output{
  2. Msg: &MyTargetType{}, // 注意指针
  3. }
  4. msg.Decode(output.Msg)

使用反射API处理任何类型:

  1. output := Output{
  2. Msg: MyTargetType{},
  3. }
  4. ...
  5. // 分配新值。
  6. v := reflect.New(reflect.TypeOf(output.Msg))
  7. // 解码到该值。注意v是指向新值的指针。
  8. msg.Decode(v.Interface()))
  9. // 将新值赋给字段。
  10. reflect.ValueOf(&output.Msg).Elem().Set(v.Elem())
英文:

The argument to Decode must be a pointer to the target type. Use this code:

  1. output := Output{
  2. Msg: &amp;MyTargetType{}, // note pointer
  3. }
  4. msg.Decode(output.Msg)

Use the reflect API to handle any type:

  1. output := Output{
  2. Msg: MyTargetType{},
  3. }
  4. ...
  5. // Allocate new value.
  6. v := reflect.New(reflect.TypeOf(output.Msg))
  7. // Decode to that value. Note that v has pointer to new value.
  8. msg.Decode(v.Interface()))
  9. // Assign new value to field.
  10. reflect.ValueOf(&amp;output.Msg).Elem().Set(v.Elem())

huangapple
  • 本文由 发表于 2023年4月27日 06:53:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/76115625.html
匿名

发表评论

匿名网友

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

确定