【问题标题】:common function to create map[string]struct from slice of struct dynamically用于从结构切片动态创建 map[string]struct 的常用函数
【发布时间】:2018-11-18 16:34:55
【问题描述】:

我有两个不同的结构,如下所述 A abd B 和两个进程函数。有什么方法可以让我编写一个通用函数来为struct 生成map[string]struct。此外,在给定结构名称的情况下,有什么方法可以使用反射来创建相同的对象?

type A struct {
    name string
    // more fields
}


type B struct {
    name string
    // more fields  
}

func ProcessA(input []A) map[string]A {
    output := make(map[string]A)
    for _, v := range input {
         output[v.name] = v
    }
  return output
}


func ProcessB(input []B) map[string]B {
    output := make(map[string]B)
    for _, v := range input {
         output[v.name] = v
    }
  return output
}

【问题讨论】:

  • 谁能解释下投票的原因?
  • 假设没有特定 cmets 的原因是 downvote 按钮的工具提示。至于问题,no Go 没有泛型,类型是不变的。是的,您可以使用反射;你试过了吗?
  • “此外,有没有什么方法使用反射给定结构名称我可以创建相同的对象?” 如果你的名字是一个字符串值,那么没有,而不仅仅是名字。您还需要一个字段列表及其类型,我也相信包名称,您仍然必须访问实际类型才能最终转换结果。
  • @JimB 是的,我确实尝试过使用界面来实现相同的目标。但后来我意识到由于https://stackoverflow.com/questions/12994679/golang-slice-of-struct-slice-of-interface-it-implements,它无法使用接口完成。那么,有什么方法可以通过使用反射来完成。而且,我还是不明白投反对票的原因。
  • @Naresh:downvote 按钮显示“这个问题没有显示任何研究工作”,并且这个问题已经被多次询问了数十次。当然,这对于反射来说是微不足道的,但在实践中这很少是一个问题,因为将简单的 for 循环内联放在几个地方比反射要清晰得多。

标签: go struct reflection


【解决方案1】:

Go 中的惯用方式是使用接口。

type Named interface {
  Name() string
}

type letter struct {
  name string
}

func (l letter) Name() string {
  return l.name
}


type A struct {
    letter
    // more fields
}


type B struct {
    letter
    // more fields  
}

func ProcessNameds(input []Named) map[string]Named {
    output := make(map[string]Named, len(input))
    for _, v := range input {
         output[v.Name()] = v
    }
  return output
}

【讨论】:

  • 它不起作用。 slice of struct != slice of interface it implements 这是参考https://stackoverflow.com/questions/12994679/golang-slice-of-struct-slice-of-interface-it-implements
  • @Naresh:这不需要地图的协方差,想法是您只使用[]Named
  • 如果需要,您可以转换回所需的类型或使用/扩展接口
  • @AlexanderTrakhimenok 不能使用 (type []A) 作为 type []Named in argument。我尝试了您的建议,但仍然出现相同的错误。正如我之前提到的,你不能在函数中传递一个结构切片,它将接口切片作为一个参数
  • 我说的是关于将 A 转换为 Named 并返回,而不是关于 []A 和 []Named。
【解决方案2】:

好吧,看看这样的事情是否有帮助:

package main

import (
    "fmt"
    "strconv"
)

type A struct {
    name string
    // more fields
}

type B struct {
    name string
    // more fields
}

func Process(x interface{}) interface{} {
    ma := make(map[string]int)
    mb := make(map[string]string)

    if x == nil {
        return nil
    } else if a, ok := x.([]A); ok {
        fmt.Printf("Type A argument passed %s\n", x)
        ma[a[0].name] = 1
        ma[a[1].name] = 2
        return ma //you can return whatever type you want here
    } else if b, ok := x.([]B); ok {
        fmt.Printf("Type B argument passed %s\n", x)
        mb[b[0].name] = "a"
        mb[b[1].name] = "b"
        return mb //you can return whatever type you want here
    } else {
        panic(fmt.Sprintf("Unexpected type %T: %v", x, x))
    }
    return nil
}

func main() {
    a := make([]A, 5)
    for i := 0; i < len(a); i++ {
        a[i].name = strconv.Itoa(i) + "A"
    }
    b := make([]B, 7)
    for i := 0; i < len(b); i++ {
        b[i].name = strconv.Itoa(i) + "B"
    }

    fmt.Println(Process(a))
    fmt.Println(Process(b))
    //Uncomment line below to see the panic
    //fmt.Println(Process(8))

}

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多