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 用windows做服务的同志有吗
  • ¥60 求一个简单的网页(标签-安全|关键词-上传)
  • ¥35 lstm时间序列共享单车预测,loss值优化,参数优化算法
  • ¥15 Python中的request,如何使用ssr节点,通过代理requests网页。本人在泰国,需要用大陆ip才能玩网页游戏,合法合规。
  • ¥100 为什么这个恒流源电路不能恒流?
  • ¥15 有偿求跨组件数据流路径图
  • ¥15 写一个方法checkPerson,入参实体类Person,出参布尔值
  • ¥15 我想咨询一下路面纹理三维点云数据处理的一些问题,上传的坐标文件里是怎么对无序点进行编号的,以及xy坐标在处理的时候是进行整体模型分片处理的吗
  • ¥15 一直显示正在等待HID—ISP
  • ¥15 Python turtle 画图