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

Accessing embedded type fields through interface

我好像错过了什么重要的东西,但我想不通是什么。我使用 reflect 通过接口访问嵌入式类型字段。我遇到的问题是,根据 runtime/pprof,它消耗了很多 CPU。我不喜欢在所有 Vehicles 上实现 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)
}

如果您知道快速的确切类型,则可以使用 type assertion,如果失败则仅恢复为反射。

例如:

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是不同的,如果你传递的值确实是一个指针,上面的例子只会"skip"反射部分:*Car.

如果有多种可能的 "acceptable" 类型,您可以使用 type switch。例如,如果您传递 Car*Car,您可以从两者中获得 Power 值。此外,如果 Engine*Engine 将被传递,则同样适用。

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

但惯用的解决方案仍然是向 Vehicle 添加一个 getter 函数:

type Vehicle interface {
    GetPower() float64
}

请注意,您不必在所有地方实施 GetPower()。如果您在 Engine:

实施它
func (e Engine) GetPower() float64 {
    return e.Power
}

并且您将 Engine 嵌入到 Car 中(就像您所做的那样),您的 Car 类型将自动在其 method set 因此它将自动实现 Vehicle。然后你的 EngineCheck() 函数将很简单:

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

Go Playground 上尝试所有这 3 种变体。