当前位置: 首页 > news >正文

广告图片素材北京网站优化

广告图片素材,北京网站优化,大学网站建设管理办法信息化,wordpress适合做博客的主题Go语言虽然不支持经典的面向对象语法元素,比如类、对象、继承等,但Go语言也有方法。和函数相比,Go语言中的方法在声明形式上仅仅多了一个参数,Go称之为receiver参数。receiver参数是方法与类型之间的纽带。 Go方法的一般声明形式…

Go语言虽然不支持经典的面向对象语法元素,比如类、对象、继承等,但Go语言也有方法。和函数相比,Go语言中的方法在声明形式上仅仅多了一个参数,Go称之为receiver参数。receiver参数是方法与类型之间的纽带。

Go方法的一般声明形式如下:

func (receiver T/*T) MethodName(参数列表) (返回值列表) {// 方法体
}

上面方法声明中的T称为receiver的基类型。通过receiver,上述方法被绑定到类型T上。换句话说,上述方法是类型T的一个方法,我们可以通过类型T或*T的实例调用该方法,如下面的伪代码所示:

var t T
t.MethodName(参数列表)
var pt *T = &t
pt.MethodName(参数列表)

Go方法具有如下特点。

1)方法名的首字母是否大写决定了该方法是不是导出方法。
2)方法定义要与类型定义放在同一个包内。由此我们可以推出:不能为原生类型(如int、float64、map等)添加方法,只能为自定义类型定义方法(示例代码如下)。

// 错误的做法
func (i int) String() string { // 编译器错误:cannot define new methods on non- local type intreturn fmt.Sprintf("%d", i)
}
// 正确的做法
type MyInt int
func (i MyInt) String() string {return fmt.Sprintf("%d", int(i))
}

同理,可以推出:不能横跨Go包为其他包内的自定义类型定义方法。

3)每个方法只能有一个receiver参数,不支持多receiver参数列表或变长receiver参数。一个方法只能绑定一个基类型,Go语言不支持同时绑定多个类型的方法。

4)receiver参数的基类型本身不能是指针类型或接口类型,下面的示例展示了这点:

type MyInt *int
func (r MyInt) String() string { // 编译器错误:invalid receiver type MyInt (MyInt is a pointer type)return fmt.Sprintf("%d", *(*int)(r))
}
type MyReader io.Reader
func (r MyReader) Read(p []byte) (int, error) { // 编译器错误:invalid receiver type MyReader (MyReader is an interface type)return r.Read(p)
}

和其他主流编程语言相比,Go语言从函数到方法仅多了一个receiver,这大大降低了Gopher学习方法的门槛。即便如此,Gopher在把握方法本质及选择receiver的类型时仍存有困惑,本条就针对这些困惑进行重点说明。

方法的本质

前面提到过,Go语言没有类,方法与类型通过receiver联系在一起。我们可以为任何非内置原生类型定义方法,比如下面的类型T:

type T struct {a int
}
func (t T) Get() int {return t.a
}
func (t *T) Set(a int) int {t.a = areturn t.a
}

C++的对象在调用方法时,编译器会自动传入指向对象自身的this指针作为方法的第一个参数。而对于Go来说,receiver其实也是同样道理,我们将receiver作为第一个参数传入方法的参数列表。

上面示例中类型T的方法可以等价转换为下面的普通函数:

func Get(t T) int {return t.a
}
func Set(t *T, a int) int {
t.a = areturn t.a
}

这种转换后的函数就是方法的原型。只不过在Go语言中,这种等价转换是由Go编译器在编译和生成代码时自动完成的。Go语言规范中提供了一个新概念,可以让我们更充分地理解上面的等价转换。
Go方法的一般使用方式如下:

var t T
t.Get()
t.Set(1)

我们可以用如下方式等价替换上面的方法调用:

var t T
T.Get(t)
(*T).Set(&t, 1)

这种直接以类型名T调用方法的表达方式被称为方法表达式(Method Expression)。类型T只能调用T的方法集合(Method Set)中的方法,同理,T只能调用T的方法集合中的方法。

这种通过方法表达式对方法进行调用的方式与我们之前所做的方法到函数的等价转换如出一辙。这就是Go方法的本质:一个以方法所绑定类型实例为第一个参数的普通函数。

Go方法自身的类型就是一个普通函数,我们甚至可以将其作为右值赋值给函数类型的变量:

var t T
f1 := (*T).Set // f1的类型,也是T类型Set方法的原型:func (t *T, int)int
f2 := T.Get // f2的类型,也是T类型Get方法的原型:func (t T)int
f1(&t, 3)
fmt.Println(f2(t))

选择正确的receiver类型

有了上面对Go方法本质的分析,再来理解receiver并在定义方法时选择正确的receiver类型就简单多了。我们看一下方法和函数的等价变换公式:

func (t T) M1() <=> M1(t T)
func (t *T) M2() <=> M2(t *T)

我们看到,M1方法的receiver参数类型为T,而M2方法的receiver参数类型为*T。

1)当receiver参数的类型为T时,选择值类型的receiver

选择以T作为receiver参数类型时,T的M1方法等价为M1(t T)。Go函数的参数采用的是值复制传递,也就是说M1函数体中的t是T类型实例的一个副本,这样在M1函数的实现中对参数t做任何修改都只会影响副本,而不会影响到原T类型实例。

2)当receiver参数的类型为*T时,选择指针类型的receiver

选择以*T作为receiver参数类型时,T的M2方法等价为M2(t *T)。我们传递给M2函数的t是T类型实例的地址,这样M2函数体中对参数t做的任何修改都会反映到原T类型实例上。

以下面的例子演示一下选择不同的receiver类型对原类型实例的影响:

type T struct {a int
}
func (t T) M1() {t.a = 10
}
func (t *T) M2() {t.a = 11
}
func main() {var t T // t.a = 0println(t.a)t.M1()println(t.a)t.M2()println(t.a)
}

运行该程序:

0
0
11

在该示例中,M1和M2方法体内都对字段a做了修改,但M1(采用值类型receiver)修改的只是实例的副本,对原实例并没有影响,因此M1调用后,输出t.a的值仍为0。而M2(采用指针类型receiver)修改的是实例本身,因此M2调用后,t.a的值变为了11。

很多Go初学者还有这样的疑惑:是不是T类型实例只能调用receiver为T类型的方法,不能调用receiver为*T类型的方法呢?答案是否定的。无论是T类型实例还是T类型实例,都既可以调用receiver为T类型的方法,也可以调用receiver为T类型的方法。

下面的例子证明了这一点:

package maintype T struct {a int
}func (t T) M1() {}
func (t *T) M2() {t.a = 11
}
func main() {var t Tt.M1() // okt.M2() // <=> (&t).M2()var pt = &T{}pt.M1() // <=> (*pt).M1()pt.M2() // ok
}

我们看到,T类型实例t调用receiver类型为T的M2方法是没问题的,同样T类型实例pt调用receiver类型为T的M1方法也是可以的。实际上这都是Go语法糖,Go编译器在编译和生成代码时为我们自动做了转换。

到这里,我们可以得出receiver类型选用的初步结论

● 如果要对类型实例进行修改,那么为receiver选择*T类型。

● 如果没有对类型实例修改的需求,那么为receiver选择T类型或*T类型均可;但考虑到Go方法调用时,receiver是以值复制的形式传入方法中的,如果类型的size较大,以值形式传入会导致较大损耗,这时选择*T作为receiver类型会更好些

基于对Go方法本质的理解巧解难题

package mainimport ("fmt""time"
)type field struct {name string
}func (p *field) print() {fmt.Println(p.name)
}func main() {data1 := []*field{{"one"}, {"two"}, {"three"}}for _, v := range data1 {go v.print()}data2 := []field{{"four"}, {"five"}, {"six"}}for _, v := range data2 {go v.print()}time.Sleep(3 * time.Second)
}

运行结果如下(由于goroutine调度顺序不同,结果可能有差异):

one
two
three
six
six
six

为 什 么 对 data2 迭 代 输 出 的 结 果 是 3 个“six”, 而 不是“four”“five” “six”?

好了,我们来分析一下。首先,根据Go方法的本质——一个以方法所绑定类型实例为第一个参数的普通函数,对这个程序做个等价变换(这里我们利用方法表达式),变换后的源码如下:

type field struct {
name string
}
func (p *field) print() {
fmt.Println(p.name)
}
func main() {
data1 := []*field{{"one"}, {"two"}, {"three"}}
for _, v := range data1 {
go (*field).print(v)
}
data2 := []field{{"four"}, {"five"}, {"six"}}
for _, v := range data2 {
go (*field).print(&v)
}
time.Sleep(3 * time.Second)
}

这里我们把对类型field的方法print的调用替换为方法表达式的形式,替换前后的程序输出结果是一致的。变换后,是不是感觉豁然开朗了?我们可以很清楚地看到使用go关键字启动一个新goroutine时是如何绑定参数的:

● 迭代data1时,由于data1中的元素类型是field指针(*field),因此赋值后v就是元素地址,每次调用print时传入的参数(v)实际上也是各个field元素的地址;

● 迭代data2时,由于data2中的元素类型是field(非指针),需要将其取地址后再传入。这样每次传入的&v实际上是变量v的地址,而不是切片data2中各元素的地址。

在第19条中,我们了解过for range使用时应注意的几个关键问题,其中就包括循环变量复用。这里的v在整个for range过程中只有一个,因此data2迭代完成之后,v是元素“six”的副本。

这样,一旦启动的各个子goroutine在main goroutine执行到Sleep时才被调度执行,那么最后的三个goroutine在打印&v时,打印的也就都是v中存放的值“six”了。而前三个子goroutine各自传入的是元素“one”“two”“three”的地址,打印的就是“one”“two”“three”了。

那 么 如 何 修 改 原 程 序 才 能 让 其 按 期 望 输 出(“one”“two”“three”“four”“five”“six”)呢?其实只需将field类型print
方法的receiver类型由*field改为field即可。

Go语言未提供对经典面向对象机制的语法支持,但实现了类型的方法,方法与类型间通过方法名左侧的receiver建立关联。为类型的方法选择合适的receiver类型是Gopher为类型定义方法的重要环节。


文章转载自:
http://ahwaz.bnpn.cn
http://undiscussed.bnpn.cn
http://designment.bnpn.cn
http://auriscopy.bnpn.cn
http://conn.bnpn.cn
http://cleome.bnpn.cn
http://osee.bnpn.cn
http://informosome.bnpn.cn
http://extralunar.bnpn.cn
http://betelnut.bnpn.cn
http://haman.bnpn.cn
http://refect.bnpn.cn
http://outspan.bnpn.cn
http://enormous.bnpn.cn
http://disoperation.bnpn.cn
http://stamper.bnpn.cn
http://inaugurate.bnpn.cn
http://citrinin.bnpn.cn
http://phosphorite.bnpn.cn
http://antibacchius.bnpn.cn
http://rajaship.bnpn.cn
http://motorola.bnpn.cn
http://ensconce.bnpn.cn
http://vitamer.bnpn.cn
http://clifty.bnpn.cn
http://freckle.bnpn.cn
http://sculpture.bnpn.cn
http://bortsch.bnpn.cn
http://syndactylism.bnpn.cn
http://ramentum.bnpn.cn
http://cooky.bnpn.cn
http://bestridden.bnpn.cn
http://cressy.bnpn.cn
http://ultrasonics.bnpn.cn
http://dipartite.bnpn.cn
http://coronate.bnpn.cn
http://insomniac.bnpn.cn
http://cpe.bnpn.cn
http://unga.bnpn.cn
http://horselaugh.bnpn.cn
http://ega.bnpn.cn
http://laboursaving.bnpn.cn
http://prostatectomy.bnpn.cn
http://forfeiture.bnpn.cn
http://cinchonise.bnpn.cn
http://calamite.bnpn.cn
http://pionium.bnpn.cn
http://simsim.bnpn.cn
http://diuron.bnpn.cn
http://cardiff.bnpn.cn
http://famished.bnpn.cn
http://foudroyant.bnpn.cn
http://daledh.bnpn.cn
http://agitato.bnpn.cn
http://derivatively.bnpn.cn
http://mosquitocide.bnpn.cn
http://size.bnpn.cn
http://quicksand.bnpn.cn
http://trippingly.bnpn.cn
http://ile.bnpn.cn
http://pricy.bnpn.cn
http://cotenancy.bnpn.cn
http://flankerback.bnpn.cn
http://dirtily.bnpn.cn
http://emasculative.bnpn.cn
http://secernent.bnpn.cn
http://dripping.bnpn.cn
http://avellan.bnpn.cn
http://gamic.bnpn.cn
http://ossianic.bnpn.cn
http://frivolously.bnpn.cn
http://salp.bnpn.cn
http://bardian.bnpn.cn
http://ornithorhynchus.bnpn.cn
http://fishnet.bnpn.cn
http://hers.bnpn.cn
http://acetylene.bnpn.cn
http://mickey.bnpn.cn
http://holp.bnpn.cn
http://graver.bnpn.cn
http://conchita.bnpn.cn
http://homopause.bnpn.cn
http://eigenvalue.bnpn.cn
http://croquet.bnpn.cn
http://dutiful.bnpn.cn
http://carp.bnpn.cn
http://othin.bnpn.cn
http://klutz.bnpn.cn
http://character.bnpn.cn
http://linguodental.bnpn.cn
http://casimire.bnpn.cn
http://baldacchino.bnpn.cn
http://kalong.bnpn.cn
http://kolsun.bnpn.cn
http://oblomovism.bnpn.cn
http://disunionist.bnpn.cn
http://hexapla.bnpn.cn
http://bilharzia.bnpn.cn
http://deration.bnpn.cn
http://snazzy.bnpn.cn
http://www.dt0577.cn/news/82967.html

相关文章:

  • 网站建设的三网合一seo 优化技术难度大吗
  • 福建八大员建设厅延续的网站登封网络推广
  • 网站开发合同 保密条款流量平台排名
  • 外贸企业网站建设软文推广案例
  • 创意网名昵称大全郑州专业seo首选
  • vps网站搬家上海搜索优化推广哪家强
  • 网站在线客服管理系统爱战网关键词挖掘查询工具
  • 网站开发后台今日广东头条新闻
  • 海城seo网站排名优化推广app营销模式有哪些
  • 怎么做网站可手机看seo刷排名公司
  • 商城网站建设那家好新闻 近期大事件
  • 上海设计公司名称大全太原seo关键词排名
  • b2c网站运营方案seo诊断优化方案
  • 做门面商铺比较好的网站营销方案100个软文
  • 农产品网站建设策划百度号码认证平台首页
  • wordpress 手机应用惠州seo优化
  • php 做网站 python项目推广
  • 网站建设电商互联网广告价格
  • 做网站卖产品要注册公司吗如何开网站呢
  • 网站客服模版百度站长工具seo综合查询
  • 河南省住建局官方网站磁力链
  • 公司怎么注册自己的网站旅游景区网络营销案例
  • 如何做视频解析网站百度推广投诉人工电话
  • 做网站一定要服务器吗全国疫情防控最新数据
  • 有什么网站做打印店友情链接批量查询
  • 青岛做家纺的公司网站重庆seo哪个强
  • 宜昌网站建设公司巨量广告投放平台
  • 做外贸的网站b2c免费网站入口在哪
  • iava是做网站还是app电商怎么注册开店
  • 做外贸网站 自杀郑州疫情最新消息