英文:
Get the type name of a generic struct without type parameters
问题
假设我有一个名为foo
的通用结构体,并从中创建了两个对象。我可以使用reflect.TypeOf()
确定每个对象的具体类型,如下所示:
package main
import (
"fmt"
"reflect"
)
type foo[T any] struct {
data T
}
func main() {
a := foo[string]{"cheese"}
b := foo[int]{42}
fmt.Println(reflect.TypeOf(a))
fmt.Println(reflect.TypeOf(b))
}
// main.foo[string]
// main.foo[int]
我想确定这些对象的通用类型(即foo
),而不是具体类型(即foo[string]
和foo[int]
)。这是否可能,还是我需要手动从这些字符串中提取通用类型(例如,使用正则表达式)?
编辑
正则表达式可能如下所示:
func GetGenericType(x any) string {
// 将类型转换为字符串
s := reflect.TypeOf(x).String()
// 运行正则表达式
r := regexp.MustCompile(`\.(.*)\[`)
// 返回匹配结果
return r.FindStringSubmatch(s)[1]
}
fmt.Println(GetGenericType(a))
fmt.Println(GetGenericType(b))
// foo
// foo
我还看到了这个问题,但它没有回答这个问题,因为它给出的是_具体_类型(即main.foo[string]
),而不是通用类型(即foo
)。
英文:
Say I have a generic struct called foo
and I create two objects from it. I can determine the concrete type of each using reflect.TypeOf()
, like so:
package main
import (
"fmt"
"reflect"
)
type foo[T any] struct {
data T
}
func main() {
a := foo[string]{"cheese"}
b := foo[int]{42}
fmt.Println(reflect.TypeOf(a))
fmt.Println(reflect.TypeOf(b))
}
// main.foo[string]
// main.foo[int]
What I am interested in is determining just the generic type of these objects (i.e., foo
) and not the concrete type (i.e., foo[string]
and foo[int]
). Is this possible or do I need to manually extract the generic type from these strings (e.g., with regex)?
Edit
Regex might look something like this:
func GetGenericType(x any) string {
// Get type as a string
s := reflect.TypeOf(x).String()
// Regex to run
r := regexp.MustCompile(`\.(.*)\[`)
// Return capture
return r.FindStringSubmatch(s)[1]
}
fmt.Println(GetGenericType(a))
fmt.Println(GetGenericType(b))
// foo
// foo
I've also seen this question but this doesn't answer this question because it gives the concrete type (i.e., main.foo[string]
) rather than the generic type (i.e., foo
).
答案1
得分: 2
反射在运行时无法看到“基础”泛型类型的名称,因为在运行时该基础类型不存在。
Go规范中相关的部分是实例化:
实例化类型会产生一个新的非泛型命名类型;实例化函数会产生一个新的非泛型函数。
因此,当你写下:
b := foo[int]{42}
name := reflect.TypeOf(b).Name()
该类型的名称确切地是foo[int]
。
值得注意的是,在编译时,不带类型参数列表的标识符 foo
是相关的,因为它防止你在同一个包中重新声明它。类型定义:
类型定义会创建一个新的、具有相同底层类型和操作的不同类型,并将一个标识符(类型名称)绑定到它上面。
TypeDef = identifier [ TypeParameters ] Type .
但是,如上所定义的实例化会产生一个与foo
不同的新命名类型;而在运行时,当你使用反射时,你只处理实例化。
总之,我认为你在正则表达式中的解决方案是可接受的,直到标准库中添加了一些辅助函数(如果有的话)。为了清晰起见,我在这里重新发布它:
func GetGenericType(x any) string {
// 将类型转换为字符串
s := reflect.TypeOf(x).String()
// 运行正则表达式
r := regexp.MustCompile(`\.(.*)\[`)
// 返回匹配结果
return r.FindStringSubmatch(s)[1]
}
只需记住Type.String()
和Type.Name()
之间的区别:任何类型都可以有一个字符串表示,但只有命名类型才有名称(显然,对吧?)。因此,例如,如果你写下:
b := &foo[int]{42}
那么b
的类型是*foo[int]
,它是一个匿名复合类型,而Name()
返回一个空字符串。
英文:
Reflection doesn't see the name of the "base" generic type, because at run time that base type doesn't exist.
The relevant passage from the Go spec is Instantiations:
> Instantiating a type results in a new non-generic named type; instantiating a function produces a new non-generic function.
So when you write:
b := foo[int]{42}
name := reflect.TypeOf(b).Name()
the name of that type is precisely foo[int]
.
It's worth noting that the identifier foo
without the type parameter list is relevant at compile time, because it prevents you from redeclaring it in the same package. Type definitions:
> A type definition creates a new, distinct type with the same
> underlying type and operations as the given type and binds an
> identifier, the type name, to it.
>
>
> TypeDef = identifier [ TypeParameters ] Type .
>
But instantiations, as defined above, result in a new named type which is different than foo
; and at run time when you can use reflection, you deal with instantiations only.
In conclusion, I think your solution with regex is acceptable, until some helper function is added to the stdlib (if ever). Reposting it here for clarity:
> func GetGenericType(x any) string {
> // Get type as a string
> s := reflect.TypeOf(x).String()
>
> // Regex to run
> r := regexp.MustCompile(\.(.*)\[
)
>
> // Return capture
> return r.FindStringSubmatch(s)[1]
> }
Just keep in mind the difference between Type.String()
and Type.Name()
: any type can have a string representation, but only named types have a name. (Obviously, right?). So for example if you wrote:
b := &foo[int]{42}
then the type of b
is *foo[int]
, which is an anonymous composite type, and Name()
returns an empty string.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论