Reflection 通过接口访问嵌入式类型字段

Reflection 通过接口访问嵌入式类型字段,reflection,go,struct,interface,embedding,Reflection,Go,Struct,Interface,Embedding,我似乎错过了一些重要的事情,但我不知道是什么。我使用reflect通过接口访问嵌入式类型字段。我遇到的问题是,根据运行时/pprof它会占用大量CPU。我不喜欢在所有车辆上实现Setter和Getter方法,所以有更好的方法吗 简化样本: package main import( "reflect" "fmt" ) // the "contract" is that all vehicles have an embedded Engine type Vehicle inter

我似乎错过了一些重要的事情,但我不知道是什么。我使用reflect通过接口访问嵌入式类型字段。我遇到的问题是,根据
运行时/pprof
它会占用大量CPU。我不喜欢在所有车辆上实现Setter和Getter方法,所以有更好的方法吗

简化样本:

package main

import(
    "reflect"
    "fmt"
)

// the "contract" is that all vehicles have an embedded Engine
type Vehicle interface {}

type Engine struct {
    Power float64
    Cubic float64
}

type Car struct {
    Engine
    Weight float64
    TopSpeed float64
}

// more Vehicles with Engines here...

func EngineCheck(v Vehicle) {
    // this does not work:
    //power := v.Power
    // reflection works but eats up a lot of CPU:
    power := reflect.ValueOf(v).Elem().FieldByName("Power").Interface().(float64)
    fmt.Println(power)
}

func main() {
    c1 := &Car{Engine{120.0, 1.2}, 1.5, 250}

    EngineCheck(c1)
}
如果您知道快速的确切类型,您可以使用,并且只有在失败时才恢复到反射

例如:

func EngineCheck(v Vehicle) {
    var power float64
    if eng, ok := v.(*Car); ok {
        power = eng.Power
    } else {
        power = reflect.ValueOf(v).Elem().FieldByName("Power").Interface().(float64)
    }
    fmt.Println(power)
}
请注意,
Car
*Car
的类型是不同的,上面的示例只会在您传递的值确实是指针时“跳过”反射部分:
*Car

如果有多种可能的“可接受”类型,则可以使用。例如,如果您传递一辆
汽车
*汽车
,则您可以从这两个位置获得
功率
值。同样,如果要传递
引擎
*引擎
,同样的情况也适用

func EngineCheck(v Vehicle) {
    var power float64
    switch i := v.(type) {
    case *Car:
        power = i.Power
    case Car:
        power = i.Power
    case *Engine:
        power = i.Power
    case Engine:
        power = i.Power
    default:
        power = reflect.ValueOf(v).Elem().FieldByName("Power").Interface().(float64)
    }
    fmt.Println(power)
}
但是惯用的解决方案仍然是在
车辆中添加一个getter函数

type Vehicle interface {
    GetPower() float64
}
请注意,您不必到处实现
GetPower()
。如果在
发动机上执行此操作

func (e Engine) GetPower() float64 {
    return e.Power
}
您将
引擎
嵌入
汽车
(就像您所做的那样),您的
汽车
类型将自动在its中包含此
GetPower()
方法(升级),因此它将自动实现
汽车
。然后您的
EngineCheck()
函数将非常简单:

func EngineCheck(v Vehicle) {
    fmt.Println(v.GetPower())
}

请在屏幕上尝试这三种变体。

我喜欢你答案中惯用的部分。这是一个直截了当的说法。谢谢你,伊卡!