Go has no unions。但工会在许多地方都是必要的。 XML过度使用了联合或选择类型。我试图找出,这是解决失踪工会的首选方法。作为示例,我尝试在XML standard中为非终端Misc
编写Go代码,该代码可以是comment,processing instruction或white space。< / p>
编写三种基本类型的代码非常简单。它们映射到字符数组和结构。
type Comment Chars
type ProcessingInstruction struct {
Target *Chars
Data *Chars
}
type WhiteSpace Chars
但是当我完成了联合的代码时,它有很多冗余函数。显然必须有一个容器结构。
type Misc struct {
value interface {}
}
为了确保容器只保存三个允许的类型,我将值设为私有,我必须为每个类型编写一个构造函数。
func MiscComment(c *Comment) *Misc {
return &Misc{c}
}
func MiscProcessingInstruction (pi *ProcessingInstruction) *Misc {
return &Misc{pi}
}
func MiscWhiteSpace (ws *WhiteSpace) *Misc {
return &Misc{ws}
}
为了能够测试联合的内容,有必要编写三个谓词:
func (m Misc) IsComment () bool {
_, itis := m.value.(*Comment)
return itis
}
func (m Misc) IsProcessingInstruction () bool {
_, itis := m.value.(*ProcessingInstruction)
return itis
}
func (m Misc) IsWhiteSpace () bool {
_, itis := m.value.(*WhiteSpace)
return itis
}
为了获得正确输入的元素,有必要编写三个getter。
func (m Misc) Comment () *Comment {
return m.value.(*Comment)
}
func (m Misc) ProcessingInstruction () *ProcessingInstruction {
return m.value.(*ProcessingInstruction)
}
func (m Misc) WhiteSpace () *WhiteSpace {
return m.value.(*WhiteSpace)
}
在此之后,我能够创建一个Misc
类型的数组并使用它:
func main () {
miscs := []*Misc{
MiscComment((*Comment)(NewChars("comment"))),
MiscProcessingInstruction(&ProcessingInstruction{
NewChars("target"),
NewChars("data")}),
MiscWhiteSpace((*WhiteSpace)(NewChars(" \n")))}
for _, misc := range miscs {
if (misc.IsComment()) {
fmt.Println ((*Chars)(misc.Comment()))
} else if (misc.IsProcessingInstruction()) {
fmt.Println (*misc.ProcessingInstruction())
} else if (misc.IsWhiteSpace()) {
fmt.Println ((*Chars)(misc.WhiteSpace()))
} else {
panic ("invalid misc");
}
}
}
你看到有很多代码看起来几乎一样。任何其他工会都会如此。所以我的问题是:有没有办法简化在Go中处理工会的方式?
通过removing redundancy来简化编程工作。但我认为上面的例子显示了完全相反的情况。我错过了什么吗?
答案 0 :(得分:8)
因为你似乎要问因为你想要类型安全,我首先会说你的初始 解决方案已经不安全了,因为你有
func (m Misc) Comment () *Comment {
return m.value.(*Comment)
}
如果你之前没有检查IsComment
,那么会发生恐慌。因此,这种解决方案没有任何好处
Volker提出的类型切换。
由于您要对代码进行分组,因此您可以编写一个函数来确定Misc
元素是什么:
func IsMisc(v {}interface) bool {
switch v.(type) {
case Comment: return true
// ...
}
}
然而,这将使您无法进行编译器类型检查。
如果您希望能够通过编译器将某些内容标识为Misc
,那么您应该这样做
考虑创建一个标记为Misc
的接口:
type Misc interface {
ImplementsMisc()
}
type Comment Chars
func (c Comment) ImplementsMisc() {}
type ProcessingInstruction
func (p ProcessingInstruction) ImplementsMisc() {}
这样你就可以编写只处理misc的函数。对象并稍后决定 你真正想要处理的事情(评论,指示......)。
如果你想模仿工会,那么你所写的方式就是我所知道的方式。
答案 1 :(得分:6)
我认为这些代码可能会减少,例如我个人认为保护type Misc
不包含“非法”内容真的很有帮助:一个简单的type Misc interface{}
会做什么,或者?
这样你就可以省去构造函数,所有Is{Comment,ProcessingInstruction,WhiteSpace}
方法都归结为类型开关
switch m := misc.(type) {
Comment: fmt.Println(m)
...
default: panic()
}
这是什么包编码/ xml用Token做的。
答案 2 :(得分:1)
我不确定你理解你的问题。 “简单”的方法就像带有接口{}的encoding / xml包。如果您不想使用接口,那么您可以像您一样做。 但是,正如您所说,Go是一种打字语言,因此应该用于打字需求。 如果你有一个结构化的XML,Go可以很合适,但你需要编写你的模式。如果你想要一个可变参数模式(一个给定的字段可以有多种类型),那么使用非类型语言可能会更好。
非常有用的json工具,可以轻松地为xml重写: http://mholt.github.io/json-to-go/
您提供了一个json输入,它为您提供了精确的Go结构。您可以有多种类型,但您需要知道哪个字段具有哪种类型。如果你不这样做,你需要使用反射,实际上你失去了Go的许多兴趣。
答案 3 :(得分:-1)
TL; DR 您不需要工会,interface{}
可以更好地解决此问题。
C中的联合用于访问特殊内存/硬件。他们还颠覆类型系统。 Go没有语言原语访问特殊内存/硬件,它也出于同样的原因避开了volatile
和位字段。
在C / C ++中,联合会也可以用于真正的低级优化/比特打包。权衡:牺牲类型系统并增加复杂性,有利于节省一些比特。这当然伴随着关于优化的所有警告。
想象一下Go有一个原生的联合类型。代码怎么会更好?用这个重写代码:
// pretend this struct was a union
type MiscUnion struct {
c *Comment
pi *ProcessingInstruction
ws *WhiteSpace
}
即使使用内置联盟访问MiscUnion
的成员,也需要进行某种运行时检查。所以使用界面并没有变得更糟。可以说interface
是优越的,因为运行时类型检查是内置的(不可能出错)并且具有非常好的语法来处理它。
联合类型的一个优点是静态类型检查,以确保只有适当的具体类型放在Misc
中。解决这个问题的Go方法是“New ...”函数,例如MiscComment
,MiscProcessingInstruction
,MiscWhiteSpace
。
以下是使用interface{}
和New*
函数的清理示例:http://play.golang.org/p/d5bC8mZAB_