在不同结构之间共享方法实现

Sharing method implementations between different structs

假设我们有 2 个结构共享一个 属性,具有相同的名称和用途,但大小不同:

type (
    L16 struct {
        Length uint16
    }

    L32 struct {
        Length uint32
    }
)

目标是使这些结构具有 GetLength 具有完全相同签名和实现的方法:

func (h *L16) GetLength() int {
    return int(h.Length)
}

func (h *L32) GetLength() int {
    return int(h.Length)
}

— 但要避免对每个结构重复实现。

所以我尝试:

type (

    LengthHolder interface {
        GetLength() int
    }

    LengthHolderStruct struct {
        LengthHolder
    }

    L16 struct {
        LengthHolderStruct
        Length uint16
    }

    L32 struct {
        LengthHolderStruct
        Length uint32
    }

)

func (h *LengthHolderStruct) GetLength() int {
    return int(h.Length)
}

— 但是 h.Length undefined (type *LengthHolderStruct has no field or method Length).

出错

我们怎么做?

粗鲁的回答是你不能你不应该。只需在每个结构上实现该方法,让未来的您和其他维护者满意。

无论如何,假设你绝对必须这样做,当然 embedded 类型对 embedding 类型一无所知所以你不能从 LengthHolderStruct.

引用 Length

就我个人而言,我认为 @mh-cbon answer 是一个不错的折衷方案。为了提供替代方案,您可以通过在嵌入式结构上将 Length 字段声明为 interface{} 并使用类型开关,以 非常丑陋 的方式解决这个问题(将类型安全扔进垃圾箱)。

我不会在我的生产系统中使用以下代码,但你可以:

func main() {
    l16 := L16{
        LengthHolderStruct: LengthHolderStruct{
            Length: uint16(200), 
            // but nothing stops you from setting uint32(200)
        },
    }
    fmt.Println(l16.GetLength())
}

type (
    LengthHolder interface {
        GetLength() int
    }

    LengthHolderStruct struct {
        Length interface{}
    }

    L16 struct {
        LengthHolderStruct
    }

    L32 struct {
        LengthHolderStruct
    }
)

func (h *LengthHolderStruct) GetLength() int {
    switch t := h.Length.(type) {
    case uint16:
        return int(t)
    case uint32:
        return int(t)
    }
    return 0
}

一旦语言获得了类型参数,你的问题就会有不同的答案:

type Constraint interface {
     type uint16, uint32
     // or `~uint16 | ~uint32` with type sets
}

type LX[T Constraint] struct {
    Length T
}

func (h *LX[T]) GetLength() int {
    return int(h.Length)
}

func main() {
    lx := LX[uint16]{
        Length: uint16(200),
    }
    fmt.Println(lx.GetLength()) // 200
}

Go2 游乐场:https://go2goplay.golang.org/p/nDZxPlXhP6H