drti52047 2016-02-15 07:35
浏览 44
已采纳

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

It seems like I missed something important but I can not figure out what it is. I use reflect to access embedded type fields through an interface. The problem I have is that according to runtime/pprof it eats up a lot of CPU. I do not like to implement Setter and Getter methods on all Vehicles so is there a better way of doing this?

Simplified sample:

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)
}
  • 写回答

1条回答 默认 最新

  • dongqia0240 2016-02-15 07:49
    关注

    You could use type assertion if you know the exact type which is fast, and only revert to reflection if that fails.

    For example:

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

    Note that the types Car and *Car are different, and the above example would only "skip" the reflection part if the value you pass is indeed a pointer: *Car.

    If there are multiple possible "acceptable" types, you could use a type switch. For example if you pass a Car or *Car, you can get the Power value from both. Also if Engine or *Engine would be passed, the same thing applies.

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

    But the idiomatic solution would still be to add a getter function to Vehicle:

    type Vehicle interface {
        GetPower() float64
    }
    

    Note that you do not have to implement GetPower() everywhere. If you implement it at the Engine:

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

    And you embed Engine into Car (as you did), your Car type will automatically have this GetPower() method (promoted) in its method set and thus it will automatically implement Vehicle. And then your EngineCheck() function would be as simple as:

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

    Try all these 3 variants on the Go Playground.

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

报告相同问题?

悬赏问题

  • ¥15 matlab实现基于主成分变换的图像融合。
  • ¥15 对于相关问题的求解与代码
  • ¥15 ubuntu子系统密码忘记
  • ¥15 信号傅里叶变换在matlab上遇到的小问题请求帮助
  • ¥15 保护模式-系统加载-段寄存器
  • ¥15 电脑桌面设定一个区域禁止鼠标操作
  • ¥15 求NPF226060磁芯的详细资料
  • ¥15 使用R语言marginaleffects包进行边际效应图绘制
  • ¥20 usb设备兼容性问题
  • ¥15 错误(10048): “调用exui内部功能”库命令的参数“参数4”不能接受空数据。怎么解决啊