有没有一种方法可以使用 reflect 来辨别 Go 中的类型关系?

Is there a way to use reflect to discern type-relationships in Go?

上下文

为 Go 语言库编写 URL 查询参数解析器

问题

据我所知,在 Go 中只有结构具有继承形式。 可以使用 reflect 包来辨别实体的类型,即类型系统中的基本存储 class,并且可以探测所述元素的类型。所以我可以辨别实体是 Kind 字符串和 Type Title,作为任意示例,假设存在这样的东西:

type Title string

更好的是,对于结构,我可以使用匿名成员来获得有限类型的继承:

type Foo struct { name string }
func (f Foo) Hello() string { return f.name; }

type Bar struct { Foo }

func main() {
  b := Bar{ Foo{"Simon"} }
  fmt.Println(b.Hello())
}

Live example

关键是,Go 允许我将 Foo 扩展为 Bar,但至少对于 Foo 的部分继承/重用 Foo 函数。

但是,对于非结构类型 - 我不知道如何解决类似于 json 或 xml 的 encoding/decoding 库的这个问题 - 我想能够将查询参数解码为结构的成员,并且至关重要的是,能够支持用户定义的类型,而无需为我的目的为每个人定义一个专门的解码器或编码器,只要它们是一种类型的派生支持我可以使用的界面。

具体来说,我想支持存储为 Google uuid.UUID 或 time.Time(或 time.Duration 的用户定义类型也很有用)。

这是一个简单的例子:

type TransactionID uuid.UUID

正如声明的那样,这继承了 uuid.UUID 的零行为,我不确定是否或如何使用反射来有意在我的 encoder/decoder 库中实现这一点?

Go 本身不会在它们之间提供任何类型的继承或关系。我的 TransactionID 不是 uuid.UUID,但它们共享相同的种类 - 一个 [16] 字节数组,据我目前所知,这就是它们共享的全部内容。

这是我的难题

如何允许我希望支持的用户定义类型 w/o 要求我的用户现在为我已经为“基本”类型定义的类型定义相同的编码器或解码器函数?

更多上下文

我的解码器库提供了这个接口:

// Parser is an interface for types that RequestParser can parse from a URL parameter
type Parser interface {
    ParseParameter(value string) error
}

我已经为 uuid.UUID 和 time.Time 以及一些其他有用的非结构类型定义了一个专门的扩展,以便从查询参数字符串中解码它们。如果我要解码的实体类型实际上是 uuid.UUID 或 time.Time 而不是基于这些的用户定义类型,那么一切正常。同样,用户别名之所以有效,是因为它们不是真正的新类型。

别名对我的目的来说太有限了——它们与别名类型没有任何有意义的区别,这严重限制了它们的实际效用。我不会回应需要使用它们的建议。谢谢

更新

理想情况下,用户应该能够为其目的定义 TransactionID,我想知道它是“一种”UUID,因此 - 默认情况下 - 使用 UUID 解析器。

用户已经很容易定义:

func (id *TransactionID) ParseParameter(value string) (err error) {
   id_, err := uuid.Parse(value)
   if err != nil {
      return
   }
   *id = TransactionID(id_)
   return
}

这将迫使我的解码器选择它们定义明确的接口 - 并完全按照它们希望的类型解码输入。

我希望 - 有一种方法可以知道它们的类型是 ____ 什么的派生类型?如果我有一个 ____ 什么的解析器 - 那么 -在没有用户定义的 ParseParameter 的情况下——使用我默认提供的智能参数(它知道 [16] 字节与 UUID 不同,并且理解更复杂的解码 UUID 的方法)。

附录

当我得到深思熟虑和有用的回复时,我会扩展这个问题并提供更多细节。我现在不确定我还能提供什么?

感谢您花时间认真回复,如果您选择这样做的话。

无法使用反射 API 来辨别一种类型是从另一种类型创建的。

specification says:

A type definition creates a new, distinct type with the same underlying type and operations as the given type, and binds an identifier to it.

The new type is called a defined type. It is different from any other type, including the type it is created from.

规范未定义已定义类型与创建它的类型之间的任何其他关系。没有要公开的反映 API 的“创建自”关系。

在例子中

type A B

反射 API 无法告诉您类型 A 是从类型 B 创建的,因为类型 A 和类型 B 之间除了共享的基础类型之外没有任何关系。

Type conversions 允许从 A 到 B 和 B 到 A,因为 A 和 B 具有相同的基础类型。

您可以使用反射 API 来确定两个类型是否具有相同的基础类型,但这对解决问题没有帮助。可能是 A 是从 B 创建的,B 是从 A 创建的,或者两者都不是从另一个创建的。在此示例中,类型 A 和 B 具有相同的基础类型:

 type A int
 type B int

问题指出解析器有一个内置的 uuid.UID 解码器。目标是将该解析器用于 type TransactionID uuid.UUID。以下是解决该问题的一些方法:

嵌入 将类型声明为 type TransactionID struct { uuid.UID }。检查具有单个已知嵌入字段的结构并进行相应处理。

Registry 添加函数函数以注册可转换类型之间的映射。

 var decodeAs  = map[reflect.Type]reflect.Type{}
 
 func RegisterDecodeAs(target, decode reflect.Type) {
     if !decode.ConvertibleTo(target) {
        panic("type must be convertible")
     }
     decodeAs[target] = decode
 }

解码时,检查decodeAs中的解码类型。如果存在,则解码为解码类型,转换为目标类型并分配。

程序启动时调用注册函数:

 func init() {
     RegisterDecodeAs(reflect.TypeOf(TransactionID(nil)), reflect.TypeOf(uuid.UUID(nil))
 }