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 一直显示正在等待HID—ISP
  • ¥15 Python turtle 画图
  • ¥15 关于大棚监测的pcb板设计
  • ¥15 stm32开发clion时遇到的编译问题
  • ¥15 lna设计 源简并电感型共源放大器
  • ¥15 如何用Labview在myRIO上做LCD显示?(语言-开发语言)
  • ¥15 Vue3地图和异步函数使用
  • ¥15 C++ yoloV5改写遇到的问题
  • ¥20 win11修改中文用户名路径
  • ¥15 win2012磁盘空间不足,c盘正常,d盘无法写入