首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Mixins的实现与编译行为的不一致性

Mixins的实现与编译行为的不一致性
EN

Stack Overflow用户
提问于 2015-02-02 15:54:13
回答 2查看 144关注 0票数 4

Mixins可以在Go (1.4.1)中使用嵌入来实现,而且由于struct{}不占用内存(据我所知),它适合于我们想要添加一些功能或只向可能与其状态无关的类型添加一个方法的情况,但我们喜欢避免使用ParseThing(...),而是编写thing.Parse(...)

因此:

代码语言:javascript
复制
type X struct{}

func (x X) F() {
    fmt.Println("functionality in X.F()")
}

type Y struct{ X }
type Z struct{ Y }

如果我们这样做了:

代码语言:javascript
复制
var z Z
z.F()

会给我们:

代码语言:javascript
复制
functionality in X.F()

到目前一切尚好。

现在,让我们使用方法F()添加另一种类型的F(),并将其嵌入到Z

代码语言:javascript
复制
type Z struct {
    Y
    OX
}

type OX struct{} // overriding X

func (x OX) F() {
    fmt.Println("functionality in OX.F()")
}

有意思的!现在我们得到了functionality in OX.F(),它显示Go编译器搜索方法,从键入self开始,然后是最后一个嵌入式类型。我们可以通过将F()添加到Z来检查这一点

代码语言:javascript
复制
func (x Z) F() {
    fmt.Println("functionality in Z.F()")
}

输出为functionality in Z.F()。现在,如果我们删除Z.F()方法并将F()添加到Y

代码语言:javascript
复制
//func (x Z) F() {
//    fmt.Println("functionality in Z.F()")
//}

func (x Y) F() {
    fmt.Println("functionality in Y.F()")
}

然后我们看到这个错误ambiguous selector z.F;通过指针重定向没有什么区别。

问题1:,为什么会这样?

间接Y的额外水平意味着其他的东西,但我想到了这一点。正如我所猜测的,func (t T) String() string{}是一个例外。此代码:

代码语言:javascript
复制
type X struct{}

func (x X) String() string {
    return "in X.String()"
}

type Y struct{ X }
type Z struct {
    Y
    OX
}

type OX struct{} // overriding X

func (x OX) String() string {
    return "in OX.String()"
}

func (x Y) String() string {
    return "in Y.String()"
}

然后这个:

代码语言:javascript
复制
var z Z
fmt.Println(z)

给我们:

代码语言:javascript
复制
{in Y.String() in OX.String()}

这是合理的。但是如果我们使用指针接收器:

代码语言:javascript
复制
import (
    "fmt"
    "testing"
)

func TestIt(t *testing.T) {
    var z Z
    fmt.Println(z)
}

type X struct{}

func (x *X) String() string {
    return "in X.String()"
}

type Y struct{ X }
type Z struct {
    Y
    OX
}

type OX struct{} // overriding X

func (x *OX) String() string {
    return "in OX.String()"
}

func (x *Y) String() string {
    return "in Y.String()"
}

将打印出来:

代码语言:javascript
复制
{{{}} {}}

问题2:为什么会这样?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-02-02 16:27:32

问题1

编译器是正确的。它应该如何决定,它应该使用哪一个OX.FY.F?因此,直接调用所需的方法是由您自己决定的:

代码语言:javascript
复制
z.Y.F()

代码语言:javascript
复制
z.OX.F()

编辑:关于为什么您的示例在定义了Y上的F之前工作,这在特辑中有提到

对于T*T类型的值x,其中T不是指针或接口类型,x.f表示存在这样一个fT中最浅的深度的字段或方法。如果没有一个深度最浅的f,则选择器表达式是非法的。

(强调后加)

在定义方法之前,最浅的实现是OX.F。在定义了Y.F之后,在同一级别上出现了两个F,这是非法的。

问题2

同样,编译器是正确的。YOX类型嵌入到Z中,而不是*Y*OX。正如用特辑写的,

对应指针类型*T的方法集是用接收方*TT声明的所有方法集(也就是说,它还包含T的方法集)。

*T拥有T的所有方法,但不是相反的方法。OXY的方法集是空的,因此很明显,fmt.Println只是打印它们,就好像它们是没有定义String()方法的任何其他类型的结构一样。

票数 8
EN

Stack Overflow用户

发布于 2015-02-02 21:59:42

Ainar-G写出简洁的答案

规格:

对于T或*T类型的值x,其中T不是指针或接口类型,x.f表示T中存在这样一个f的最浅深度的字段或方法。如果没有一个深度最浅的f,则选择器表达式是非法的。

我想再加一点

规格:

如果S包含一个匿名字段T,则S和*S的方法集都包含对接收方T的提升方法。*S的方法集还包括带有接收器*T的提升方法。

因此,如果您只使用引用来促进方法,如

代码语言:javascript
复制
fmt.Println(&z)

但这将导致选择的模糊性,因为字符串方法的可能性很小,因此由于规范的原因,选择器字符串是非法的。编译器必须抱怨,但它没有。这种行为似乎没有具体说明,只能解释为特殊情况下的普通打印操作在我的脑海中。这将如预期的那样工作。

代码语言:javascript
复制
var y Y
fmt.Println(&y)

下面是工作示例游乐场

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28281564

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档