douzhuolong9886 2017-04-05 16:18
浏览 45
已采纳

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

The goal is

Having a variable B that satisfies a specific interface I through pointer receivers type, create another variable C (with reflection and using B), copy B's values into C, modify C (without changing B) and return C as type I.

Suppose that I have the following types, the following snippets mimic production code:

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() {}

And I have a method that does the following:

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
}

It compiles and runs however with the following TestFunction it gives me this error message:

interface conversion: One is not IFace: missing method C [recovered]
    panic: interface conversion: One is not IFace: missing method C

and the test function:

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")
   }
}

Is there a way to have an interface{} type that wraps a struct value become an interface{} type that wraps a struct pointer without using the underlying struct type directly, like avoiding: var any interface{} = aliased.(*One) ??,

Could the unsafe package be of help here?

Here is a playground that replicates the panic, thanks to RayfenWindspear

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

  • 写回答

1条回答 默认 最新

  • dongyue1988 2017-04-05 19:56
    关注

    Not sure if this is what you want, but if I understood you correctly, in your updated example from the comments the returned base is what you want to copy, modify, and then return the copy of, without changing anything in base. If that's the case, this code aliased = base is not gonna do what you want if the base's underlying type is a pointer, which is true in this case.

    Note, I've modified the var names to better reflect your assignment.

    // 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, 世界"))
    

    Here's the playground: https://play.golang.org/p/bGTdy2vYUu

    Sorry if this not what you're looking for.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 数学建模招标中位数问题
  • ¥15 phython路径名过长报错 不知道什么问题
  • ¥15 深度学习中模型转换该怎么实现
  • ¥15 HLs设计手写数字识别程序编译通不过
  • ¥15 Stata外部命令安装问题求帮助!
  • ¥15 从键盘随机输入A-H中的一串字符串,用七段数码管方法进行绘制。提交代码及运行截图。
  • ¥15 TYPCE母转母,插入认方向
  • ¥15 如何用python向钉钉机器人发送可以放大的图片?
  • ¥15 matlab(相关搜索:紧聚焦)
  • ¥15 基于51单片机的厨房煤气泄露检测报警系统设计