doufei1852 2019-01-15 00:56
浏览 53
已采纳

将字段名称反映到特定界面

I have a struct type with a number of fields that all implement a Renderer interface. The field types implement the interface with pointer receivers. I would like to have a function that takes the field name and calls the Render method on that field. I am able to locate the field and get lots of information about it, but doing the type assertion seems to be biting me because of the pointer receivers. Here's some code that shows my problem:

package main

import (
    "fmt"
    "reflect"
)

type Renderer interface {
    Render()
}

type First struct {
    ExampleField Field
}

type Field []int

func (f *Field) Render() {
    fmt.Println("Hello from first")
}

func main() {
    f := First{
        Field{1, 2, 3},
    }
    f.ExampleField.Render()
    renderField("ExampleField", &f)
    renderField2("ExampleField", &f)
}

func renderField(field string, f *First) {
    structVal := reflect.ValueOf(*f)
    renderType := reflect.TypeOf((*Renderer)(nil)).Elem()
    fieldToRender := structVal.FieldByName(field)
    fieldPtr := reflect.PtrTo(fieldToRender.Type())
    fmt.Printf("Implements? %v
", fieldPtr.Implements(renderType))
    fmt.Printf("Addressable? %v
", fieldToRender.CanAddr())
    fieldInter := fieldToRender.Interface()
    if renderer, ok := fieldInter.(Renderer); ok {
        // Pointer receiver so this never gets called
        fmt.Print("Able to cast")
        renderer.Render()
    }
}

func renderField2(field string, f *First) {
    structVal := reflect.ValueOf(*f)
    fieldToRender := structVal.FieldByName(field)
    vp := reflect.New(reflect.TypeOf(fieldToRender))
    vp.Elem().Set(reflect.ValueOf(fieldToRender))
    vpAddr := vp.Elem().Addr()
    typeVal := vpAddr.Interface()
    fmt.Println(typeVal) // <main.Field Value>⏎
    renderer := typeVal.(Renderer)
    renderer.Render()
    // interface conversion: *reflect.Value is not main.Renderer: missing method Render
}

renderField2 seems to get me close but Addr() gives me a *Reflect.Value and when I call Interface() that seems to be the underlying type instead. If I switch to a non-pointer receiver then the first function works. I found reflect value Interface and pointer receiver which seems to be almost exactly what I'm asking, and the question is answered but if I actually call the isZeroer method presented in the playground link it's always false so it doesn't actually seem to answer the question.

It seems like Addr is the key because it specifically mentions pointer receivers but I'm struggling to coerce it back into an interface.

  • 写回答

1条回答 默认 最新

  • dongzizhi9903 2019-01-15 01:40
    关注

    Use this code:

    func renderField(name string, f *First) {
        structVal := reflect.ValueOf(f).Elem()
        field := structVal.FieldByName(name).Addr().Interface()
        if renderer, ok := field.(Renderer); ok {
            renderer.Render()
        }
    }
    

    The key point is to change:

    structVal := reflect.ValueOf(*f)
    

    to:

    structVal := reflect.ValueOf(f).Elem()
    

    The statement used in the question creates a non-addressable struct value. The fields in a non-addressable struct are also not addressable, therefore it's not possible to access the pointer receiver on the fields.

    The statement used in this answer creates an addressable struct value.

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

报告相同问题?

悬赏问题

  • ¥15 表达式必须是可修改的左值
  • ¥15 如何绘制动力学系统的相图
  • ¥15 对接wps接口实现获取元数据
  • ¥20 给自己本科IT专业毕业的妹m找个实习工作
  • ¥15 用友U8:向一个无法连接的网络尝试了一个套接字操作,如何解决?
  • ¥30 我的代码按理说完成了模型的搭建、训练、验证测试等工作(标签-网络|关键词-变化检测)
  • ¥50 mac mini外接显示器 画质字体模糊
  • ¥15 TLS1.2协议通信解密
  • ¥40 图书信息管理系统程序编写
  • ¥20 Qcustomplot缩小曲线形状问题