目标是
通过指针接收器类型使变量
B
满足特定接口I
,创建另一个变量C
(使用反射并使用B
),复制{{1将{}值改为B
,修改C
(不更改C
)并将B
作为C
类型返回。
假设我有以下类型,以下代码段模仿生产代码:
I
我有一个方法可以执行以下操作:
import (
"reflect"
)
type IFace interface {
A()
B()
C()
}
type Meta struct {
s string
}
func (m *Meta) A() {}
func (m *Meta) B() {}
func (m *Meta) C() {}
type One struct {
M *Meta
B bool
}
func (o *One) A() {}
func (o *One) B() {}
func (o *One) C() {}
它编译并运行以下TestFunction它给我这个错误消息:
func Alias(src, dest *Meta) (IFace, error) {
base, err := find(src) //asume that `find` is implemented and err is nil
if err != nil {
return err
}
// trouble starts here ...
// allocate new "instance"
aliased := reflect.New(reflect.TypeOf(base)).Elem().Interface()
// copy the base value
aliased = base
aliasedV := reflect.ValueOf(aliased).Elem()
fm := aliasedV.FieldByName("M")
fm.Set(reflect.ValueOf(dest))
return aliasedV.Interface().(Iface), nil
}
和测试功能:
interface conversion: One is not IFace: missing method C [recovered]
panic: interface conversion: One is not IFace: missing method C
是否有一种方法可以将包含结构值的func TestOne(t *testing.T) {
srcID := &Meta{S: "SRC"}
destID := &Meta{S: "DEST"}
aliased, err := Alias(srcID, destID)
if err != nil {
t.Error(err)
}
one, isOne := aliased.(*One)
if !isOne {
t.Error("fail")
}
}
类型变为interface{}
类型,该类型包含结构指针而不直接使用基础结构类型,例如避免:interface{}
??,
var any interface{} = aliased.(*One)
包可以在这里提供帮助吗?
这是一个复制恐慌的游乐场,感谢RayfenWindspear
答案 0 :(得分:1)
不确定这是否是您想要的,但如果我理解正确,则在评论的更新示例中,返回的base
是您要复制,修改,然后返回的副本,而不是改变base
中的任何内容。如果是这种情况,如果基础的基础类型是指针,则此代码aliased = base
将不会执行您想要的操作,在这种情况下也是如此。
注意,我已经修改了var名称以更好地reflect
你的作业。
// Having a variable B that satisfies a specific
// interface I through pointer receivers type.
B, err := find(src)
if err != nil {
return nil, err
}
// create another variable C (with reflection and using B),
C := reflect.New(reflect.TypeOf(B).Elem())
// copy B's values into C
bv := reflect.ValueOf(B).Elem()
for i :=0; i < bv.NumField(); i++ {
fv := bv.Field(i)
if fv.Kind() == reflect.Ptr {
v := reflect.New(fv.Elem().Type()) // allocate a new pointer of the same type as the B's pointer field is pointing to, in this case 'Meta'
v.Elem().Set(fv.Elem()) // set the newly allocated pointer's value to the same value as B's pointer field is pointing to, in this case 'Meta{S: "SRC"}'
C.Elem().Field(i).Set(v) // set the newly allocated pointer as the C's field
} else {
C.Elem().Field(i).Set(fv) // non pointer field? just set and that's it
}
// NOTE: if B's field's have subfields that are pointers you'll have to do this for all of them if you want a deep copy
}
// modify C (without changing B)
C.Elem().FieldByName("M").Elem().FieldByName("S").Set(reflect.ValueOf("Hello, 世界"))
这是游乐场:https://play.golang.org/p/bGTdy2vYUu
很抱歉,如果这不是你想要的。