简体   繁体   English

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

[英]Go Reflect field name to specific interface

I have a struct type with a number of fields that all implement a Renderer interface. 我有一个带有许多字段的struct类型,这些字段都实现了Renderer接口。 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. 我想要一个具有字段名称并在该字段上调用Render方法的函数。 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\n", fieldPtr.Implements(renderType))
    fmt.Printf("Addressable? %v\n", 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. renderField2似乎使我接近,但是Addr()给我一个* Reflect.Value,当我调用Interface()时,它似乎是基础类型。 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. 我发现反射值接口和指针接收器似乎与我要问的几乎完全相同,并且回答了该问题,但是如果我实际上调用了游乐场链接中提供的isZeroer方法,则它始终为false,因此实际上似乎没有答案问题。

It seems like Addr is the key because it specifically mentions pointer receivers but I'm struggling to coerce it back into an interface. 似乎Addr是关键,因为它专门提到了指针接收器,但是我正在努力将其强制返回接口。

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. 此答案中使用的语句创建一个可寻址的结构值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM