通过接口访问嵌入式类型字段
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 种变体。
我好像错过了什么重要的东西,但我想不通是什么。我使用 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 种变体。