是否可以更改接口定义的指针类型和变量值?
我可以使用反射更改指针值:v.Elem().Set(reflect.ValueOf(&Greeter{"Jack"}).Elem())
等效于a = &Greeter{"Jack"}
。
但是我怎样才能使反射等效a = &Greeter2{"Jack"}
呢?
更新:不幸的是,在我要解决的实际问题中,我无法解决原始变量(panic: reflect: reflect.Value.Set using unaddressable value
),这就是为什么我要尝试在指针级别进行变通。
这是完整的示例代码:
package main
import (
"fmt"
"reflect"
)
type Greeter struct {
Name string
}
func (g *Greeter) String() string {
return "Hello, My name is " + g.Name
}
type Greeter2 struct {
Name string
}
func (g *Greeter2) String() string {
return "Hello, My name is " + g.Name
}
func main() {
var a fmt.Stringer
a = &Greeter{"John"}
v := reflect.ValueOf(a)
v.Elem().Set(reflect.ValueOf(&Greeter{"Jack"}).Elem())
//v.Elem().Set(reflect.ValueOf(&Greeter2{"Jack"}).Elem()) // panic: reflect.Set: value of type main.Greeter2 is not assignable to type main.Greeter
fmt.Println(a.String()) // Hello, My name is Jack
a = &Greeter2{"Ron"}
fmt.Println(a.String()) // Hello, My name is Ron
}
Go中的所有内容都是通过价值传递的。接口也。当您传递接口类型的值时,将创建接口值的副本(及其(value;type)
内部),并且您将只能修改该副本,而不会影响原始副本。
您具有a
接口类型的变量。如果要修改存储在此变量中的值,则必须传递/使用此变量的地址。
首先,让我们修改Greeter2.String()
方法以知道哪个被调用:
func (g *Greeter2) String() string {
return "Hello2, My name is " + g.Name
}
并a.String()
在首次初始化后调用,以查看它最初确实包含一个*Greeter
值,因为我们将很快对其进行更改。
var a fmt.Stringer
a = &Greeter{"John"}
fmt.Println(a.String()) // Hello, My name is John
v := reflect.ValueOf(&a).Elem()
v.Set(reflect.ValueOf(&Greeter2{"Jack"}))
fmt.Println(a.String()) // Hello2, My name is Jack
a = &Greeter2{"Ron"}
fmt.Println(a.String()) // Hello2, My name is Ron
输出(在Go Playground上尝试):
Hello, My name is John
Hello2, My name is Jack
Hello2, My name is Ron
因此,本质上没有更改接口 值 的值和类型的事情,仅更改了可能属于接口类型的变量(具有内存地址)中的数据。
所以关键的事情是:
reflect.ValueOf(&a)
*Greeter2
实现fmt.String
而没有实现Greeter
。有关更多详细信息和深入的介绍,请阅读:The Go Blog:反射法则
编辑:关于修改副本
我的回答的第一部分:每当您传递某些内容时,都会制作一份副本。这还包括将接口值传递给reflect.ValueOf()
:还将接收一个副本,因此您无法避免这种情况。进行这项工作的唯一方法是传递一个
指针 。因为指针也将被复制,但我们正在修改的 尖 值(不是指针),这是一样的。
问题内容: 在用于golang的mongodb驱动程序中,包含以下代码: 接口清零器的定义如下: 当我实现我的结构 有用。但是,当我使用指针接收器实现IsZero方法时: 类型声明失败,并且IsZero无法执行。 谁可以给我解释一下这个? 问题答案: 据推测某处之上的有上的开关 如果您在Reflection包中查看,则此处的文档 是其中一种,又是另一种。在switch语句中,它不匹配,因为该方法的
问题内容: 解释我的问题的最短方法是该代码: 但是,如果我尝试将指向Item的指针设置为i,代码将起作用: 但是我的函数接收对象,而不是指向它的指针。我可以得到它的类型(第一)。但是当我尝试指向它的指针时,我收到的是,而不是我的Object()的指针。 我可以指向该对象进行调用吗?或者可能是其他方式 问题答案: 切勿使用指向接口的指针。如果您需要一个指针来使用指针接收器调用方法,则必须将指针放入。
查看如下程序:nexter是一个接口类型,并且定义了一个next()方法读取下一字节。函数nextFew将nexter接口作为参数并读取接下来的num个字节,并返回一个切片:这是正确做法。但是nextFew2使用一个指向nexter接口类型的指针作为参数传递给函数:当使用next()函数时,系统会给出一个编译错误:*n.next undefined (type nexter has no fiel
问题内容: 例如下面的示例: DO 和两个满足,即使一个是一个指针,另一个是直接一个结构? 问题答案: 通过编译代码很容易得到答案: 该错误是基于以下规格要求: 接收器类型必须采用T或 T的形式,其中T是类型名称。用T表示的类型称为接收方基本类型; 它不能是指针或接口类型* ,并且必须在与方法相同的包中声明。 (强调我的) 声明: 声明一个 指针 类型,即 没有资格作为方法接收者。
Go语言程序中对指针获取反射对象时,可以通过 reflect.Elem() 方法获取这个指针指向的元素类型。这个获取过程被称为取元素,等效于对指针类型变量做了一个 操作,代码如下: 代码输出如下: name: '' kind: 'ptr' element name: 'cat', element kind: 'struct' 代码说明如下: 第 15 行,创建了cat结构体的实例,ins 是一个
问题内容: 我有一个函数,可以遍历作为参数传递的接口的所有字段。为了实现这一点,我正在使用反射。问题是我不知道如何获取非指针字段的地址。这是一个例子: 上面的代码代表了我的测试结构。现在,这是遍历指定结构并列出有关其详细信息的实际函数: 这是结构实例化/初始化之后的实际测试: 最后是InspectStruct调用的输出: 如您所见,我正在使用递归,因此,如果其中一个字段是结构类型,则可以为其调用I