您当前的位置:首页 > 电脑百科 > 程序开发 > 程序员

深入理解Go的interface内部执行原理

时间:2020-06-21 21:51:19  来源:  作者:

Go的interface是由两种类型来实现的: iface 和 eface

iface指的是接口中申明有方法(至少1个),eface表示接口中没有申明方法

后面会讲到这两个到底是什么,所以这里需要先不用关心。

深入理解

下面是一个简单的Demo,Binary实现了fmt.Stringer接口,我们调用 ToString() 方法,会调用接口的 String() 方法。

// 类型
type Binary uint64

// 实现String方法,实现fmt.Stringer接口
func (i Binary) String() string {
	return strconv.FormatUint(uint64(i), 10)
}

func main() {
	b := Binary(200) // 01
  // var b Binary = Binary(200)
	ToString(b)// 02
}
func ToString(value interface{}) string {
  // 断言,转化成fmt.Stringer接口类型
	newValue, ok := value.(fmt.Stringer) //3
	if ok {
		return newValue.String()// 4
	}
	panic("The value  is not implement fmt.Stringer func")
}

大致的执行流程图下所示:

//01 执行的是,在内存中开辟一块内存,存放200这个值

深入理解Go的interface内部执行原理

 

// 02 调用ToString方法,首先方法传递过程中需要隐式将b转换成interface{}类型,实际上做的就是以下:

深入理解Go的interface内部执行原理

 

首先大家可能会关心,我就没见过这个结构,你是不是骗人的,其实有这个结构体,是在 runtime/runtime2.go 中

type eface struct {
	_type *_type // 类型
	data  unsafe.Pointer //值
}

那么如何转换的呢?

type 指得是 Binary的类型,包含了Binary类型的所有信息(后面会介绍到)

data 指向的真实数据,由于我们传递的不是指针,所以这种情况下其实是做了一次内存拷贝( 所以也就是尽可能的别使用interface{} ),data其实存的是拷贝的数据,如果换做是指针,其实也是拷贝了一份指针地址(这也就是reflect.Elem方法的作用)

​ 以下这几段代码全部来自于 runtime/iface.go

// 关于 unsafe.Pointer,unsafe包学习的时候介绍过
func convT2E(t *_type, elem unsafe.Pointer) (e eface) {
	if raceenabled {
		raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2E))
	}
	if msanenabled {
		msanread(elem, t.size)
	}
  // 首先会分配一块内存,内存大小为类型t的大小,下面这段话是mallocgc的介绍
  // Allocate an object of size bytes.
  // Small objects are allocated from the per-P cache's free lists.
  // Large objects (> 32 kB) are allocated straight from the heap.
	x := mallocgc(t.size, t, true)
	// TODO: We allocate a zeroed object only to overwrite it with actual data.
	// Figure out how to avoid zeroing. Also below in convT2Eslice, convT2I, convT2Islice.
  // 将elem拷贝到x 
	typedmemmove(t, x, elem)
  // eface 的类型为t,值为x
	e._type = t
	e.data = x
	return
}

//3 其次就是到了断言的部分,那么断言到底执行了什么呢?

// inter 指的是fmt.Stringer接口类型信息
// e 就是我们上面的的interface{} 的真实类型eface
func assertE2I2(inter *interfacetype, e eface) (r iface, b bool) {
	t := e._type
	if t == nil {
		return
	}
  // 获取tab,其实大家有可能不太理解
	tab := getitab(inter, t, true)
	if tab == nil {
		return
	}
	r.tab = tab
	r.data = e.data
	b = true
	return
}

那么这里就需要理解什么是 iface

type iface struct {
	tab  *itab//table
	data unsafe.Pointer //值
}

tab 又是什么?

​ tab的意思是table的意思,关于table的概念,大家可以去找找资料
​ 具有方法的语言通常属于以下两种阵营之一:为所有方法调用静态地准备表(如在C ++和JAVA中),或在每次调用时进行方法查找(如在Smalltalk及其许多模仿程序中,包括JavaScriptPython)以及添加奇特的缓存以提高调用效率。Go位于两者的中间:它具有方法表,但在运行时对其进行计算。
type itab struct {
	inter *interfacetype// 接口类型,这里就是Stringer
	_type *_type// 值类型,	这里就是Binary
	hash  uint32 // copy of _type.hash. Used for type switches.
	_     [4]byte
	fun   [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.
}

其实上面这段代码的流程如下:

data就是 eface.data

tab 其实就是 : inter 指的是接口类型(也就是fmt.Stringer接口), type 是Binary类型, fun[0] 是(Binary)String方法 ,其他几个先不用care

深入理解Go的interface内部执行原理

 

//4 newValue.String() 到底做了啥,其实根据上面我们很容易知道,无法就是 newValue.tab.fun[0].(newValue.data) ,所以就是这么简单。

总结

1、go的 interface{} 转换过程中至少做一次内存拷贝,所以传递指针是最好的选择。

type User struct {
	Name string
	Age  int
}

func main() {
  var empty interface{} = User{} //这里会拷贝一次,将user转换成interface{},所以函数传递过程中也别直接使用结构体传递
}

正确写法

func main() {
	var empty interface{} = &User{}
}

2、有人会问到字符串传递是否内存拷贝,回答否,因为字符串底层是一个 byte[] 数组,他的结构组成是

type StringHeader struct {
	Data uintptr //数据,是一个二进制数组
	Len  int// 长度
}

所以64位计算机,字符串的长度是128位,占用16个字节

3、减少使用 interface{} ,因为会有不必要的开销,其次Golang本身是一个强类型语言,静态语言,申明式是最好的方式。

4、 interface{} 是反射的核心,后期我会讲解反射



Tags:Go的interface   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
Go的interface是由两种类型来实现的: iface 和 efaceiface指的是接口中申明有方法(至少1个),eface表示接口中没有申明方法后面会讲到这两个到底是什么,所以这里需要先不用关心。...【详细内容】
2020-06-21  Tags: Go的interface  点击:(43)  评论:(0)  加入收藏
▌简易百科推荐
我 2010 年开始在 Github 上开源自己的代码。在 push 代码之前我根本没想过为什么。只是因为我当时学了 git,而且我又觉得 Github 很方便,可以用来备份自己的代码。而后我就参...【详细内容】
2021-12-28  程序员的喵    Tags:Github   点击:(2)  评论:(0)  加入收藏
JAVA开发工程师(北京)本科 3-5年经验 面议 (招1人)岗位职责:1.负责我行应用系统的设计,完成软件编码工作,负责管理代码设计规范等工作;2.根据应用需求分析说明书,评估需求研发的可行...【详细内容】
2021-12-27  just do丶IT公众号    Tags:国企   点击:(2)  评论:(0)  加入收藏
今天聊聊编程的本质。程序就是数据结构+控制+逻辑,程序员编程工作的本质是翻译,翻译机要来了,程序员怎么办?黑客帝国中的程序黑客帝国4就要上映了,不知道前三部你看懂了么?值得多...【详细内容】
2021-12-17  博士聊IT    Tags:程序员   点击:(9)  评论:(0)  加入收藏
梦醒之后,每个人对于这份职业的未来、互联网行业的未来,以及更重要的,自己的未来都有了更现实的判断 文 | 祝颖丽编辑 | 黄俊杰一个生于 1986 年的人,他所走过的前半生:从出生起,...【详细内容】
2021-12-03    财经杂志  Tags:程序员   点击:(16)  评论:(0)  加入收藏
前些天在头条看到一个八二年的哥们,述说自己找工作屡次被拒的问题,在网上引起了广泛的讨论,这件事给我留下了很深的印象,因为这哥们和我同是程序员,都人到中年,上有老下有小。唯一...【详细内容】
2021-12-01  云南贤哥在深圳    Tags:程序员   点击:(20)  评论:(0)  加入收藏
很多读者都问过一个问题:程序员如何实现高速成长?之前也写过相关的文章,强调的主要是夯实计算机体系基础知识。 再说另一个诀窍:多看经典开源项目,这些项目大多是众多顶尖程序员...【详细内容】
2021-11-30  findyi    Tags:程序员   点击:(15)  评论:(0)  加入收藏
近日,一位45岁的网民在中国政府网留言求职,引发关注。该网民自称是一名软件开发人员,今年45岁,精通各种技术体系,“而我辞职回家半年后再回来寻找工作机会的时候,却发现连个面试...【详细内容】
2021-11-17  郭主任    Tags:程序员   点击:(42)  评论:(0)  加入收藏
即使在安全技术取得进步之后,网络犯罪仍在不断增加。据统计,网络犯罪每分钟给企业造成约 290 万美元的损失。主要是因为新技术不断涌现,难以维护安全。随着网络威胁的增加,网络...【详细内容】
2021-11-04  章大千    Tags:编程语言   点击:(40)  评论:(0)  加入收藏
北漂小伙李强(化名),在北京互联网大厂工作7年,月薪3万,离职回老家开摄影店,亏了200万。李强出生于山西一座名不经传的小城市,互联网专业大学毕业的他,没有听父母的劝言回到家乡考公...【详细内容】
2021-10-29  霸王课  今日头条  Tags:程序员   点击:(53)  评论:(0)  加入收藏
程序员是青春饭,这在国内似乎是公认的。所以很多公司不愿招大龄程序员,很多程序员也“知趣”地及早转型。有的做管理,有的做架构,我还见过改行卖保险的。总之,年龄大了不想敲代码...【详细内容】
2021-10-27  编程的艺术    Tags:   点击:(30)  评论:(0)  加入收藏
相关文章
    无相关信息
最新更新
栏目热门
栏目头条