如何使用反射将struct值转换为struct指针

时间:2017-04-05 16:18:47

标签: go reflection types interface

目标是

  

通过指针接收器类型使变量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

https://play.golang.org/p/860uAE7qLc

1 个答案:

答案 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

很抱歉,如果这不是你想要的。