想象我有以下结构:
type MyGeneric[T string | int] struct {
}
我想在创建新的 MyGeneric 时检查用于实例化该结构的泛型是字符串还是 int。
myGenericString := MyGeneric[string]{}
myGenericString.canHandle("hello") -> should return true
myGenericString.canHandle(8) -> should return false
func (mG MyGeneric[T]) canHandle(value any) bool {
// how to get what T is the same type as value
}
对于普通值,只需直接实例化
T
即可获取其值并reflect.TypeOf()
。但我们可以声明一个 [0]T
并采用它的元素类型,因为:
[0]T
占用0内存,而T
可能与T
一样大,如果T
类似于[4096]int
,这会浪费大量堆栈和堆1。
T
不适用于接口类型。 reflect.TypeOf()
在nil接口值(无论是否为空接口)上将返回reflect.Type(nil)
,后续使用将导致恐慌。package main
import (
"fmt"
"reflect"
)
type MyGeneric[T any] struct {
}
func (mG MyGeneric[T]) canHandle(value any) bool {
var zero [0]T
tt := reflect.TypeOf(zero).Elem()
vt := reflect.TypeOf(value)
fmt.Printf("-> %v covers %v\n", tt, vt)
return vt.AssignableTo(tt)
}
type empty struct{}
func main() {
fmt.Printf("%v\n", MyGeneric[string]{}.canHandle(""))
fmt.Printf("%v\n", MyGeneric[any]{}.canHandle(""))
fmt.Printf("%v\n", MyGeneric[string]{}.canHandle(1))
fmt.Printf("%v\n", MyGeneric[MyGeneric[struct{}]]{}.canHandle(MyGeneric[struct{}]{}))
fmt.Printf("%v\n", MyGeneric[MyGeneric[struct{}]]{}.canHandle(MyGeneric[empty]{}))
}
输出:
-> string covers string
true
-> interface {} covers string
true
-> string covers int
false
-> main.MyGeneric[struct {}] covers main.MyGeneric[struct {}]
true
-> main.MyGeneric[struct {}] covers main.MyGeneric[main.empty]
false
1 不确定它是否可以优化,或者是否在堆或堆栈或两者上分配,因为它在向上转换为
reflect.TypeOf
后被传递到 interface{}
。
尚未实施。有一个关于向 reflect.Type
添加必要方法的
开放提案。
从 Go 1.19 开始,当前的解决方法是解析从
TypeOf
获得的字符串。像这样的东西:
var r = regexp.MustCompile("[A-Za-z0-9_]+\\.[A-Za-z0-9_]+\\[(.*)\\]")
func (mG MyGeneric[T]) typeParam(value any) {
tname := reflect.TypeOf(mG).String() // this is `main.MyGeneric[string]`
match := r.FindStringSubmatch(tname)
fmt.Println(match[1]) // string
}
如果目标只是获取类型参数的名称。这不太好,因为它取决于类型的字符串表示形式。从好的方面来说,它不会强迫您思考如果使用接口实例化
T
会发生什么。
如果您需要使用
T
类型进行进一步计算,例如将其与其他类型进行比较等。@SOFe 的答案提供了一个不依赖于任意字符串表示的解决方案。
但是要注意用接口实例化的
T
:另请参阅在Golang中,如何将作为泛型类型的接口与nil进行比较?
另一个解决方案是在结构中添加一个 placeHolder 字段,可用于通过类型开关获取其类型以避免反射:
type MyGeneric[T string | int] struct {
placeHolder T
}
func (mG MyGeneric[T]) canHandle(value any) bool {
switch t1 := any(p.placeHolder).(type) {
case any:
switch t2 := value.(type) {
case any:
return t1 == t2
}
}
return false
}