【发布时间】:2021-04-27 17:41:50
【问题描述】:
上下文
为 Go 语言库编写 URL 查询参数解析器
问题
据我所知,Go 中只有结构具有继承形式。 可以使用反射包来识别实体的种类,即它是类型系统中的基本存储类,并且可以探测所述元素的类型。所以我可以辨别实体是 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())
}
重点是,Go 允许我将 Foo 扩展为 Bar,但至少对于 Foo 的部分继承/重用 Foo 函数。
但是,对于非结构类型 - 我不知道如何处理类似于 json 或 xml 的编码/解码库的这个问题 - 我希望能够将查询参数解码为结构的成员,并且至关重要的是,能够支持用户定义的类型,而不需要每个人都为我的目的定义一个专门的解码器或编码器,只要它们是支持我可以使用的接口的类型的派生。
具体来说,我希望支持存储为 Google uuid.UUID 或 time.Time(或 time.Duration 也很有用)的用户定义类型。
这是一个简单的例子:
type TransactionID uuid.UUID
正如声明的那样,这继承了 uuid.UUID 的零行为,我不确定是否或如何使用反射来故意在我的编码器/解码器库中实现这一点?
Go 本身不会提供它们之间的任何继承或关系。我的 TransactionID 不是 uuid.UUID,但它们共享同一种类型 - 一个 [16] 字节数组,据我目前所知,这就是它们共享的全部内容。
这是我的难题
如何允许我希望支持的用户定义类型不要求我的用户现在为他们的类型定义我已经为“基本”类型定义的相同编码器或解码器函数?
更多上下文
我的解码器库使这个接口可用:
// 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 的方法)。
附录
当我得到深思熟虑和有用的答复时,我将扩展此问题并提供更多详细信息。我现在不确定我还能提供什么?
感谢您抽出宝贵时间认真回复,如果您选择这样做的话。
【问题讨论】:
-
当您将一种类型嵌入到另一种类型中时,封闭类型将获得与嵌入类型相同的方法。当您基于另一个命名类型定义新的命名类型时,新类型不会获得基类型的方法。第二个不是类型别名。你的问题不清楚。 “关系”是什么意思?
-
“我知道只有结构在 Go 中具有继承形式。” ——嗯,不是真的。 Go 中根本没有继承性。但是有嵌入,也许这就是你所说的“一种形式”。但是您可以嵌入 any 类型,而不仅仅是结构,但只能嵌入 到 结构中。
-
如果你想支持更多类型,然后在接口中封装一个用于转换和编码的逻辑,并将处理程序存储在解析器中,然后循环遍历它们,或者只存储一个并让用户决定他们想要的后端使用。
-
真正问题的答案:反射 API 无法告诉您 A 被定义为类型 B,因为除了共享的底层类型之外,类型 A 和 B 之间没有任何关系。使用结构嵌入来声明一个与其他类型共享方法的新类型。
-
@Mordachai 允许转换,因为基础类型相同。 TransactionID 和 uuid UUID 之间没有其他关系。