获取没有类型参数的泛型结构体的类型名称

huangapple go评论69阅读模式
英文:

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.

huangapple
  • 本文由 发表于 2022年12月18日 09:05:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/74838506.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定