首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在go (1.18)中的泛型上实现多态实现的最佳方法是什么?

在go (1.18)中的泛型上实现多态实现的最佳方法是什么?
EN

Stack Overflow用户
提问于 2022-03-19 21:52:14
回答 2查看 370关注 0票数 3

我想要创建一个向量类型,它对其内部数据是通用的,但是在给定输入类型时,方法的实现方式可能有所不同。

代码语言:javascript
复制
type SupportedType interface {
         ~int64 | ~uint64 |  ~float64 | string | bool | time.Time
}

type Vec[T SupportedType] struct {
    data []T
}

我想根据类型在函数上添加不同的实现。例如:

代码语言:javascript
复制
func (vec Vec[T]) Sort() {
    ...
}

在大多数泛型类型中,<将很好地工作。但是,如果是T -> time.Time,我想使用Before方法,如果是T --> bool,那么我希望所有的假值都放在true之前。

我对如何做到这一点有一些想法,但是在新的仿制世界里,什么是“惯用的”呢?我的应用程序对性能敏感。

对所有函数都相同的类型使用类型联合不起作用(https://play.golang.com/p/QWE-XteWpjL)。

在类型特定的结构中嵌入容器确实有效( https://play.golang.com/p/j0AR48Mto-a ),但需要使用接口,这意味着示例函数中的LessVal不能内联。如果类型联合中的子集之间没有清晰的描述,那么它也可能不能很好地工作。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2022-03-19 23:31:00

顺便说一下,已经有一个用于排序的库了

https://pkg.go.dev/golang.org/x/exp/slices#Sort

1.您可以使用泛型创建接口,然后键入assert。

示例:

代码语言:javascript
复制
type Lesser[T SupportedType] interface {
    Less(T) bool
}

type Vec[T SupportedType] []T

func (vec Vec[T]) Less(a, b int) bool {
    return any(vec[a]).(Lesser[T]).Less(vec[b])
}

func main() {
    vs := Vec[String]([]String{"a", "b", "c", "d", "e"})
    vb := Vec[Bool]([]Bool{false, true})
    fmt.Println(vs.Less(3, 1))
    fmt.Println(vb.Less(0, 1))
}

游乐场1

2.您可以将类型保存在Vec上。

示例:

代码语言:javascript
复制
type Lesser[T SupportedType] interface {
    Less(T) bool
}

type Vec[T SupportedType, L Lesser[T]] []T

func (vec Vec[T, L]) Less(a, b int) bool {
    return any(vec[a]).(L).Less(vec[b])
}

func main() {
    vs := Vec[String, String]([]String{"a", "b", "c", "d", "e"})
    fmt.Println(vs.Less(3, 1))
}

游乐场2

3.嵌套类型约束

谢谢@blackgreen

例子:

代码语言:javascript
复制
type SupportedType interface {
    Int8 | Time | Bool | String
}

type Lesser[T SupportedType] interface {
    Less(T) bool
}

type Vec[T interface {
    SupportedType
    Lesser[T]
}] []T

func (vec Vec[T]) Less(a, b int) bool {
    return vec[a].Less(vec[b])
}

func main() {
    vs := Vec[String]([]String{"a", "b", "c", "d", "e"})
    fmt.Println(vs.Less(3, 1))
}

playgrond 3

基准:

代码语言:javascript
复制
benchmark 1 : 28093368          36.52 ns/op       16 B/op          1 allocs/op

benchmark 2 : 164784321          7.231 ns/op           0 B/op          0 allocs/op

benchmark 3 : 212480662          5.733 ns/op           0 B/op          0 allocs/op

Embedding a container inside type specific structs:
benchmark 4 : 211429621          5.720 ns/op           0 B/op          0 allocs/op

这取决于你哪一个最适合你。但国际海事组织3号是最好的。

票数 2
EN

Stack Overflow用户

发布于 2022-03-20 09:31:53

就我个人而言,我认为最好不要在联合中包含许多彼此无关的类型,因为它们不会共享许多公共操作,并且您最终会编写特定于类型的代码。那么使用仿制药有什么意义.?

无论如何,可能的策略取决于SupportedType约束的类型集中包含了哪些内容,以及您希望如何处理这些约束:

只精确类型,不使用方法

T上使用类型开关,并运行对具体类型有意义的任何操作。当方法实现只使用一个类型为T的值时,效果最好,因为您可以直接使用开关保护(v := any(vec[a]).(type))中的变量。当您在开关保护器中的值旁边有更多的T值时,它就不再漂亮了,因为您必须单独地转换和断言所有这些值:

代码语言:javascript
复制
func (vec Vec[T]) Less(a, b int) bool {
    switch v := any(vec[a]).(type) {
    case int64:
        return v < any(vec[b]).(int64)

    case time.Time:
        return v.Before(any(vec[b]).(time.Time))

    // more cases...
    }
    return false
}

与方法

将包含方法的接口参数化,并将其T约束为受支持的类型。然后将Vector的类型参数限制在两个上,其优点是确保不能用忘记实现Less(T) bool的类型实例化Vector,并消除类型断言,否则,在运行时会出现恐慌。

代码语言:javascript
复制
type Lesser[T SupportedType] interface {
    Less(T) bool
}

type Vec[T interface { SupportedType; Lesser[T] }] []T

func (vec Vec[T]) Less(a, b int) bool {
    return vec[a].Less(vec[b])
}

带有方法和预先声明类型的()

不可能。请考虑以下几点:

代码语言:javascript
复制
type SupportedTypes interface {
    // exact predeclared types
    int | string
}

type Lesser[T SupportedTypes] interface {
    Less(T) bool
}

约束Lesser有一个空类型集,因为intstring都不能有方法。在这里,您回到了“精确类型而没有方法”的情况。

具有近似类型(**~T**)

将上述约束更改为近似类型:

代码语言:javascript
复制
type SupportedTypes interface {
    // approximate types
    ~int | ~string
}

type Lesser[T SupportedTypes] interface {
    Less(T) bool
}

类型切换不是一个选项,因为case ~int:是不合法的。而约束上存在的方法阻止您使用预先声明的类型实例化:

代码语言:javascript
复制
Vector[MyInt8]{} // ok when MyInt8 implements Lesser
Vector[int8]     // doesn't compile, int8 can't implement Lesser

因此,我看到的选择是:

  • 强制客户端代码使用定义的类型,在许多情况下这可能是很好的。
  • 将约束范围缩小为支持相同操作的类型
  • 反射(通过基准测试来查看性能损失是否太大),但是反射实际上无法找到底层类型,因此您可以使用reflect.KindCanConvert进行一些黑客操作。

当/如果这一提案通过时,这可能会改进,甚至可能超过其他选项。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71542373

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档