英文:
Get type parameter from a generic struct using reflection
问题
假设我有以下结构体:
type MyGeneric[T string | int] struct {
}
我想要检查在创建一个新的MyGeneric实例时,用于实例化该结构体的泛型是字符串还是整数。
myGenericString := MyGeneric[string]{}
myGenericString.canHandle("hello") -> 应该返回true
myGenericString.canHandle(8) -> 应该返回false
func (mG MyGeneric[T]) canHandle(value any) bool {
// 如何获取T与value的类型相同
}
英文:
Imagine I have the following struct:
type MyGeneric[T string | int] struct {
}
I want to check whether the generic used to instantiate that struct was a string or a int when creating a new MyGeneric.
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
}
答案1
得分: 9
只需直接实例化T
以获取其值。
type MyGeneric[T any] struct {
}
func (mG MyGeneric[T]) canHandle(value any) bool {
var t T
tt := reflect.TypeOf(t)
vt := reflect.TypeOf(value)
fmt.Printf("-> %v == %v\n", tt, vt)
return tt == vt
}
type empty struct{}
func main() {
fmt.Printf("%v\n", MyGeneric[string]{}.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 == string
true
-> string == int
false
-> main.MyGeneric[struct {}] == main.MyGeneric[struct {}]
true
-> main.MyGeneric[struct {}] == main.MyGeneric[main.empty]
false
如果你担心T
会分配太多未使用的堆栈空间,可以将其改为数组形式:
var tArr [0]T
tt := reflect.TypeOf(tArr).Elem()
英文:
Just instantiate the T
directly to get its value.
type MyGeneric[T any] struct {
}
func (mG MyGeneric[T]) canHandle(value any) bool {
var t T
tt := reflect.TypeOf(t)
vt := reflect.TypeOf(value)
fmt.Printf("-> %v == %v\n", tt, vt)
return tt == vt
}
type empty struct{}
func main() {
fmt.Printf("%v\n", MyGeneric[string]{}.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]{}))
}
Output:
-> string == string
true
-> string == int
false
-> main.MyGeneric[struct {}] == main.MyGeneric[struct {}]
true
-> main.MyGeneric[struct {}] == main.MyGeneric[main.empty]
false
If you are worried about T
allocating too much unused stack, make it an array instead:
var tArr [0]T
tt := reflect.TypeOf(tArr).Elem()
答案2
得分: 3
这还没有被实现。关于向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() // 这是 `main.MyGeneric[string]`
match := r.FindStringSubmatch(tname)
fmt.Println(match[1]) // string
}
如果目标只是获取类型参数的名称,这个方法还不错,因为它依赖于类型的字符串表示。好的一面是,它不会让你考虑如果使用接口实例化T
会发生什么。
如果你需要对T
的类型进行进一步的计算,例如与其他类型进行比较等,@SOFe的回答提供了一个不依赖于任意字符串表示的解决方案。
但是要注意使用接口实例化的T
:参见https://stackoverflow.com/questions/74000242/in-golang-how-to-compare-interface-as-generics-type-to-nil
英文:
It hasn't been implemented yet. There is an open proposal about adding the necessary methods to reflect.Type
.
The current workaround as of Go 1.19 is to parse the string obtained from TypeOf
. Something like this:
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
}
This if the goal is just to obtain the name of the type parameter. It's not great, as it depends on the type's string representation. On the bright side, it doesn't force you to think about what happens if you instantiate T
with interfaces.
If you need to do further computations with the type of T
, e.g. compare it to other types etc. @SOFe’s answer provides a solution that doesn’t depend on arbitrary string representations.
However watch out for T
instantiated with interfaces: see also https://stackoverflow.com/questions/74000242/in-golang-how-to-compare-interface-as-generics-type-to-nil
答案3
得分: 2
另一种解决方案是在结构体中添加一个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
}
英文:
Another solution is to add a placeHolder field to your struct, that can be used to get its type via type switch to avoid reflect:
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
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论