【问题标题】:Golang generics - simple use caseGolang 泛型 - 简单用例
【发布时间】:2019-05-04 08:26:03
【问题描述】:

假设我有 3 个结构:

type A struct{
   Foo map[string]string
}

type B struct{
   Foo map[string]string
}

type C struct{
   Foo map[string]string
}

然后我想创建一个可以接受任何这些结构的函数:

func handleFoo (){

}

有没有办法用 Golang 做到这一点?比如:

type ABC = A | B | C

func handleFoo(v ABC){
   x: = v.Foo["barbie"] // this would be nice!
}

好的,让我们尝试一个界面:

type FML interface {
  Bar() string
}

func handleFoo(v FML){
   z := v.Bar() // this will compile
   x: = v.Foo["barbie"] // this won't compile - can't access properties like Foo from v
}

在一种鼓励/强制组合的语言中,我无法理解为什么你不能访问像 Foo 这样的属性。

【问题讨论】:

  • 技术上我不认为这是泛型本身,它与类型层次结构或接口有关
  • 在任何其他情况下,当你告诉自己“我需要泛型”时,准备好像疯子一样复制粘贴(或代码生成,这实际上是同样糟糕的解决方案)
  • 对于问题中显示的类型和handleFoo,可以使用func handleFoo(v struct{ Foo map[string]string })See it in the Playground。这种方法有局限性。例如,handleFoo 无权访问类型 A、B 或 C 中的任何方法。
  • Go 没有泛型。试图伪造它是一个愚蠢的想法。您的代码的目标是什么?让我们专注于您的问题,而不是您不受支持的解决方案。事实上,这是一个XY Problem
  • 接受不同的类型很容易。这就是接口的用途。

标签: go


【解决方案1】:

你可以这样使用接口,添加一个方法GetFoo来获取每个struct的foo。

type A struct{
    Foo map[string]string
}

func(a *A) GetFoo() map[string]string {
    return a.Foo
}

type B struct{
    Foo map[string]string
}

func(b *B) GetFoo() map[string]string {
    return b.Foo
}

type C struct{
    Foo map[string]string
}

func(c *C) GetFoo() map[string]string {
    return c.Foo
}

type ABC interface {
    GetFoo() map[string][string]
}

func handleFoo (v ABC){
    foo := v.GetFoo()
    x:=foo["barbie"]
}

【讨论】:

  • 是的,这行得通,因为它们都基本相同。精确的。类型。大声笑..很好的答案。
  • 如果一个是map[string]string,另一个是map[string]int,另一个是map[string]bool,它会起作用吗?我不这么认为
  • 值是不同的类型。界面应更改为GetFoo() map[string]interface
  • @AlexanderMills 在您自己的问题示例代码中,它们都是完全相同的类型。如果您需要不同的东西,请编辑问题以澄清这一点。
【解决方案2】:

因为 A、B 和 C 都是 assignable 到相同的底层类型,所以您可以使用带有该底层类型参数的函数:func handleFoo(v struct{ Foo map[string]string })

Run it on the playground.

这种方法的一个限制是 A、B 和 C 上的方法(即使具有相同的名称和签名)在 handleFoo 中不可用。

【讨论】:

  • 是的,这只有在所有 3 个结构都具有完全相同的类型时才有效,但除非出现更好的东西,否则很快就会接受这个答案
  • 就像我说的,从技术上讲,我认为这不是 generics 本身,但你认为这是什么语言功能?
  • 这些类型必须可分配给相同的基础类型。类型可以与问题中的不同,并在链接的游乐场示例中显示。可分配性规范的部分是here
  • A、B 和 C 是不同的类型,但它们具有相同的底层类型。 Go 不允许将A 分配给B,但AB 都可以分配给struct{ Foo map[string]string }
  • 语言特性是可分配性。
【解决方案3】:

您可以尝试reflect 并将interface{} 传递给handleFoo

https://play.golang.org/p/sLyjDvVrUjQ

https://golang.org/pkg/reflect/

package main

import (
    "fmt"
    "reflect"
)

func main() {
    type A struct {
        Foo map[string]string
    }
    type B struct {
        Foo map[string]int
    }
    type C struct {
        Foo map[string]uint
    }
    a := A{
        Foo: map[string]string{"a":"1"},
    }

    b := B{
        Foo: map[string]int{"a":2},
    }

    c := C {
        Foo: map[string]uint{"a":3},
    }


    fmt.Println(a, b, c)

    handleFoo(a)
    handleFoo(b)
    handleFoo(c)

    fmt.Println(a, b, c)
}



func handleFoo(s interface{}) {
    v := reflect.ValueOf(s)
    foo := v.FieldByName("Foo")
    if !foo.IsValid(){
        fmt.Println("not valid")
        return
    }

    switch foo.Type() {
    case reflect.TypeOf(map[string]string{}):
        fmt.Println("is a map[string]string")
        foo.Interface().(map[string]string)["a"] = "100"
    case reflect.TypeOf(map[string]int{}):
        fmt.Println("is a map[string]int")
        foo.Interface().(map[string]int)["a"] =  200
    case reflect.TypeOf(map[string]uint{}):
        fmt.Println("is a map[string]uint")
        foo.Interface().(map[string]uint)["a"] =  300
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-09-27
    • 1970-01-01
    • 1970-01-01
    • 2017-08-18
    • 1970-01-01
    • 2023-03-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多