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

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

Having a hard time with reflection in golang

问题

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

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

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

   msg <- receive() //发送给我一个类型为p2p.Msg的消息

   var message MyTargetType
   msg.Decode(&message) //这个工作正常,这显然是使用函数的正确方式,使用变量的指针

   anotherMessage := output.Msg //这是一个interface{},见下面
   msg.Decode(&anotherMessage) //这个失败了

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

package main

import (
	"fmt"
	"reflect"
)

type mystruct struct {
	hello string
}

func main() {
	var first mystruct
	second := mystruct{}

	fmt.Println(reflect.TypeOf(first))
	fmt.Println(reflect.TypeOf(second))
}

这将打印相同的类型:

main.mystruct
main.mystruct

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

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

type Output struct {
  Msg interface{}
}

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

output := Output{
    Msg: MyTargetType{}.
}
//

anotherOutput := Output{
    Msg: AnotherType{}.
}
//等等

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

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

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

甚至是

   var decodedMsg = reflect.New(reflect.TypeOf(output.Msg)).Elem().Interface()
   fmt.Println(reflect.TypeOf(decodedMsg)) //这实际上打印出了正确的类型!!!
   // 但是Decode仍然失败,显示:
   // 给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:

   msg &lt;- receive() //sends me a message of type p2p.Msg

   var message MyTargetType
   msg.Decode(&amp;message) // this works correctly and this is apparently the correct way to use the function, with a pointer to the variable

   anotherMessage := output.Msg // this is an interface{}, see below
   msg.Decode(&amp;anotherMessage) // this fails

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

package main

import (
	&quot;fmt&quot;
	&quot;reflect&quot;
)

type mystruct struct {
	hello string
}

func main() {
	var first mystruct
	second := mystruct{}

	fmt.Println(reflect.TypeOf(first))
	fmt.Println(reflect.TypeOf(second))
}

This prints the types to be the same:

main.mystruct
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:

type Output struct {
  Msg interface{}
}

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

output := Output{
    Msg: MyTargetType{}.
}
//

anotherOutput := Output{
    Msg: AnotherType{}.
}
// 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.

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

or even

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

答案1

得分: 1

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

output := Output{
    Msg: &MyTargetType{},  // 注意指针
}

msg.Decode(output.Msg)

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

output := Output{
    Msg: MyTargetType{},
}

...

// 分配新值。
v := reflect.New(reflect.TypeOf(output.Msg))
// 解码到该值。注意v是指向新值的指针。
msg.Decode(v.Interface()))
// 将新值赋给字段。
reflect.ValueOf(&output.Msg).Elem().Set(v.Elem())
英文:

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

output := Output{
    Msg: &amp;MyTargetType{},  // note pointer
}

msg.Decode(output.Msg)

Use the reflect API to handle any type:

output := Output{
    Msg: MyTargetType{},
}

...

// Allocate new value.
v := reflect.New(reflect.TypeOf(output.Msg))
// Decode to that value.  Note that v has pointer to new value.
msg.Decode(v.Interface()))
//  Assign new value to field.
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:

确定