如何在Go语言中将类型传递给函数参数

30

错误:类型CustomStruct不是表达式。

type CustomStruct struct {
}

func getTypeName(t interface{}) string {
    rt := reflect.TypeOf(t).Elem()
    return rt.Name()
}

getTypeName(CustomStruct)

如何在不使用类型实例的情况下将结构体类型传递给函数?
以下代码可行:
getTypeName((*CustomStruct)(nil))

但我想知道是否有更简单的版本..

使用 reflect.Type - thwd
2
只是好奇,但是你为什么想在运行时获取名称的类型?如果只是为了输出,你可以使用带有golang格式的fmt.Sprintffmt.Printf("%#v", var),它会打印出类似于main.CustomStruct{}(类型+值)的内容。 - Elias Van Ootegem
4
这个问题听起来非常像 XY 问题。你为什么想要这样做呢? - Jonathan Hall
3个回答

39
你不能这样做。你只能传递一个值,而CustomStruct不是一个值,而是一种类型。使用类型标识符会导致编译时错误。
通常情况下,当要传递一个“类型”时,你需要传递一个描述该类型的reflect.Type值。这就是你在getTypeName()中“创建”的内容,但getTypeName()将变得没有太多可做的事情:
func getTypeName(t reflect.Type) string {
    return t.Name()
}

// Calling it:
getTypeName(reflect.TypeOf(CustomStruct{}))

请注意,对于匿名类型(例如[]int),此方法返回一个空字符串。

另一种方法是传递一个“类型化”的nil指针值,就像您所做的那样。但同样地,您也可以使用一个类型化的nil值来创建reflect.Type,而不必创建相应类型的值,例如:

t := reflect.TypeOf((*CustomStruct)(nil)).Elem()
fmt.Println(t.Name()) // Prints CustomStruct

我已经像这样提到过,getTypeName((*CustomStruct)(nil))。 - Lion.k
3
是的,我知道并且已经提到了(“正如你所做的”)。没有其他方法,你不能传递一个类型标识符,因为它不是一个值。 - icza
如何在函数的实现中使用t来实例化CustomStruct? - user1663023
@QianChen 例如 reflect.New(t),它会返回一个指向 CustomString 的指针,该指针被包装在 reflect.Value 中。 - icza
@QianChen,虽然有点晚了,但如果您仍在寻找,给定类型T,“reflect.New(T)。Type()。Elem()”会为T提供一个反射类型。 - Sam Hughes
假设我不想使用通用类型,而是想传递一个具体的类型。那么如何传递一个整数类型呢?使用 reflect.TypeOf(int) 并不能实现,但是使用 reflect.TypeOf(0) 可以。 - Lord of Scripts

8

让我们重新开始吧!

Go语言的泛型提案已经获得批准,并即将推出。当这个问题第一次被问到时,这可能更合理,但是对于任何想要立即实现通用模式的人来说,我认为我有一个不错的API。

目前,您无法与抽象类型进行交互,但是您可以与抽象类型上的方法进行交互,并且反射允许您检查函数签名。 对于一个方法,第0个是接收器。

type Example struct {int}
type Generic struct{reflect.Type}

func (p Example) Type() {}

func Reflect(generic interface{}) Generic {
    real := reflect.TypeOf(generic)
    if real.Kind() != reflect.Func || real.NumIn() < 1 {
        panic("reflect.Type.In(n) panics if not a func and if n out of bounds")
    }
    return Generic{real.In(0)}
}

func (g Generic) Make() interface{} {
    return reflect.Zero(g.Type).Interface()
}

func main() {
    tOfp := Reflect(Example.Type)
    fmt.Printf("Name of the type: %v\n", tOfp.Name()) 
    fmt.Printf("Real (initial)value: %v\n", tOfp.Make())
}

一些快速笔记:

  1. "Example" 的结构并不重要,重要的是它具有一个非指针接收器的方法。
  2. 定义一个名为 "Generic" 的类型作为结构体是为了实现我认为 OP 实际想要的东西。
  3. 上述关于 "Generic" 的定义是一个结构体而不是一个接口,以便它可以拥有自己的方法集。将 "Generic" 定义为一个接口,并使用与每个操作数类型特定的方法集会使得意义更加明显。

如果您不知道,Go 1.18 将带来真正的泛型功能。我上面的示例没有 linter 或编译保护,如果使用不正确,运行时会导致恐慌。它确实有效,并且让您在等待原生实现时推理抽象类型。

愉快的编码!


哇,这似乎即使我为Example创建父/基结构并在那里定义Type方法也能工作。更接近Java的虚拟继承。 需要进行更多实验,但很有前途,谢谢! - Павел
哇哦,这似乎即使我为Example创建了父/基本结构并在那里定义了Type方法也能工作。更接近Java的虚拟继承。 需要进行更多实验,但前景看好,谢谢! - undefined

4
从 Go 版本 1.18 开始引入了一个新功能 泛型。在大多数情况下,我们可以使用泛型而不是将类型传递给函数。这样,我们还可以获得编译时错误而不是运行时错误,并且它比 reflect 更高效。

示例代码

func HttpGet[T](url, body) T {
    var resp T
    return T
}

resp := HttpGet[ResponseType]("dummy.example", nil)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接