分析接口的赋值,反射,断言的实现原理

版本:golang v1.12

interface底层使用2个struct表示的:efaceiface

一:接口类型分为2个

1. 空接口

//比如
var i interface{}

2. 带方法的接口

//比如
type studenter interface {
GetName() string
GetAge() int
}

二:eface 空接口定义

空接口通过eface结构体定义实现,位于src/runtime/runtime2.go

type eface struct {
_type *_type //类型信息
data unsafe.Pointer //数据信息,指向数据指针
}

可以看到上面eface包含了2个元素,一个是_type,指向对象的类型信息,一个 data,数据指针

三:_type 结构体

_type 位于 src/runtime/type.go


_type 是go里面所有类型的一个抽象,里面包含GC,反射,大小等需要的细节,它也决定了data如何解释和操作。
里面包含了非常多信息 类型的大小、哈希、对齐以及种类等自动。


所以不论是空eface和非空iface都包含 _type 数据类型

type _type struct {
size uintptr //数据类型共占用的空间大小
ptrdata uintptr //含有所有指针类型前缀大小
hash uint32 //类型hash值;避免在哈希表中计算
tflag tflag //额外类型信息标志
align uint8 //该类型变量对齐方式
fieldalign uint8 //该类型结构字段对齐方式
kind uint8 //类型编号
alg *typeAlg //算法表 存储hash和equal两个操作。map key便使用key的_type.alg.hash(k)获取hash值
// gcdata stores the GC type data for the garbage collector.
// If the KindGCProg bit is set in kind, gcdata is a GC program.
// Otherwise it is a ptrmask bitmap. See mbitmap.go for details.
gcdata *byte //gc数据
str nameOff // 类型名字的偏移
ptrToThis typeOff
}

_type 中的一些数据类型如下:

// typeAlg is 总是 在 reflect/type.go 中 copy或使用.
// 并保持他们同步.
type typeAlg struct {
// 算出该类型的Hash
// (ptr to object, seed) -> hash
hash func(unsafe.Pointer, uintptr) uintptr
// 比较该类型对象
// (ptr to object A, ptr to object B) -> ==?
equal func(unsafe.Pointer, unsafe.Pointer) bool
}
type nameOff int32
type typeOff int32

但是各个类型需要的类型描叙是不一样的,比如chan,除了chan本身外,还需要描述其元素类型,而map则需要key类型信息和value类型信息等:

//src/runtime/type.go

type ptrtype struct {
typ _type
elem *_type
} type chantype struct {
typ _type
elem *_type
dir uintptr
} type maptype struct {
typ _type
key *_type
elem *_type
bucket *_type // internal type representing a hash bucket
keysize uint8 // size of key slot
valuesize uint8 // size of value slot
bucketsize uint16 // size of bucket
flags uint32
}

看上面的类型信息,第一个自动都是 _type,接下来也定义了一堆类型所需要的信息(如子类信息),这样在进行类型相关操作时,可通过一个字(typ *_type)即可表述所有类型,然后再通过_type.kind可解析出其具体类型,最后通过地址转换即可得到类型完整的”_type树”,参考reflect.Type.Elem()函数:

// reflect/type.go
// reflect.rtype结构体定义和runtime._type一致 type.kind定义也一致(为了分包而重复定义)
// Elem()获取rtype中的元素类型,只针对复合类型(Array, Chan, Map, Ptr, Slice)有效
func (t *rtype) Elem() Type {
switch t.Kind() {
case Array:
tt := (*arrayType)(unsafe.Pointer(t))
return toType(tt.elem)
case Chan:
tt := (*chanType)(unsafe.Pointer(t))
return toType(tt.elem)
case Map:
tt := (*mapType)(unsafe.Pointer(t))
return toType(tt.elem)
case Ptr:
tt := (*ptrType)(unsafe.Pointer(t))
return toType(tt.elem)
case Slice:
tt := (*sliceType)(unsafe.Pointer(t))
return toType(tt.elem)
}
panic("reflect: Elem of invalid type")
}

四:没有方法的interface赋值后内部结构

对于没有方法的interface赋值后的内部结构是怎样的呢?
可以先看段代码:

import (
"fmt"
"strconv"
) type Binary uint64 func main() {
b := Binary(200)
any := (interface{})(b)
fmt.Println(any)
}

输出200,赋值后的结构图是这样的:

图片来自:https://blog.csdn.net/i6448038/article/details/82916330

对于将不同类型转化成type万能结构的方法,是运行时的convT2E方法,在runtime包中。
以上,是对于没有方法的接口说明。
对于包含方法的函数,用到的是另外的一种结构,叫iface

五:iface 非空接口

iface结构体表示非空接口:

iface

// runtime/runtime2.go
// 非空接口
type iface struct {
tab *itab
data unsafe.Pointer //指向原始数据指针
}

itab

itab结构体是iface不同于eface,比较关键的数据结构

// runtime/runtime2.go
// 非空接口的类型信息
type itab struct {
//inter 和 _type 确定唯一的 _type类型
inter *interfacetype // 接口自身定义的类型信息,用于定位到具体interface类型
_type *_type // 接口实际指向值的类型信息-实际对象类型,用于定义具体interface类型
hash int32 //_type.hash的拷贝,用于快速查询和判断目标类型和接口中类型是一致
_ [4]byte
fun [1]uintptr //动态数组,接口方法实现列表(方法集),即函数地址列表,按字典序排序
//如果数组中的内容为空表示 _type 没有实现 inter 接口 }

属性interfacetype类似于_type,其作用就是interface的公共描述,类似的还有maptypearraytypechantype…其都是各个结构的公共描述,可以理解为一种外在的表现信息。interfacetype源码如下:

// runtime/type.go
// 非空接口类型,接口定义,包路径等。
type interfacetype struct {
typ _type
pkgpath name
mhdr []imethod // 接口方法声明列表,按字典序排序
} // 接口的方法声明,一种函数声明的抽象
// 比如:func Print() error
type imethod struct {
name nameOff // 方法名
ityp typeOff // 描述方法参数返回值等细节
} type nameOff int32
type typeOff int32

method 存的是func 的声明抽象,而 itab 中的 fun 字段才是存储 func 的真实切片。

非空接口(iface)本身除了可以容纳满足其接口的对象之外,还需要保存其接口的方法,因此除了data字段,iface通过tab字段描述非空接口的细节,包括接口方法定义,接口方法实现地址,接口所指类型等。iface是非空接口的实现,而不是类型定义,iface的真正类型为interfacetype,其第一个字段仍然为描述其自身类型的_type字段。

六:iface整体结构图

图片来自:https://blog.csdn.net/i6448038/article/details/82916330

七:含有方法的interface赋值后的内部结构

含有方法的interface赋值后的内部结构是怎样的呢?

package main

import (
"fmt"
"strconv"
) type Binary uint64
func (i Binary) String() string {
return strconv.FormatUint(i.Get(), 10)
} func (i Binary) Get() uint64 {
return uint64(i)
} func main() {
b := Binary(200)
any := fmt.Stringer(b)
fmt.Println(any)
}

首先,要知道代码运行结果为:200。
其次,了解到fmt.Stringer是一个包含String方法的接口。

type Stringer interface {
String() string
}

最后,赋值后接口Stringer的内部结构为:

八:参考:

https://wudaijun.com/2018/01/go-interface-implement/
https://blog.csdn.net/i6448038/article/details/82916330#comments

深入理解Go语言(01): interface源码分析的更多相关文章

  1. 《深入理解Spark-核心思想与源码分析》(一)总体规划和第一章环境准备

    <深入理解Spark 核心思想与源码分析> 耿嘉安著 本书共计486页,计划每天读书20页,计划25天完成. 2018-12-20   1-20页 凡事豫则立,不豫则废:言前定,则不跲:事 ...

  2. Vue系列---理解Vue.nextTick使用及源码分析(五)

    _ 阅读目录 一. 什么是Vue.nextTick()? 二. Vue.nextTick()方法的应用场景有哪些? 2.1 更改数据后,进行节点DOM操作. 2.2 在created生命周期中进行DO ...

  3. 深入理解 Node.js 中 EventEmitter源码分析(3.0.0版本)

    events模块对外提供了一个 EventEmitter 对象,即:events.EventEmitter. EventEmitter 是NodeJS的核心模块events中的类,用于对NodeJS中 ...

  4. SpringBoot与Mybatis整合方式01(源码分析)

    前言:入职新公司,SpringBoot和Mybatis都被封装了一次,光用而不知道原理实在受不了,于是开始恶补源码,由于刚开始比较浅,存属娱乐,大神勿喷. 就如网上的流传的SpringBoot与Myb ...

  5. 《深入理解Spark-核心思想与源码分析》(四)第四章存储体系

    天行健,君子以自强不息:地势坤,君子以厚德载物.——<易经> 本章导读 Spark的初始化阶段.任务提交阶段.执行阶段,始终离不开存储体系. Spark为了避免Hadoop读写磁盘的I/O ...

  6. 《深入理解Spark-核心思想与源码分析》(六)第六章计算引擎

    RDD是Spark对各类数据计算模型的统一抽象,被用于迭代计算过程以及任务输出结果的缓存读写. 在所有MapReduce框架中,shuffle是连接map任务和reduce任务的桥梁.shuffle性 ...

  7. 《深入理解Spark-核心思想与源码分析》(二)第二章Spark设计理念和基本架构

    若夫乘天地之正,而御六气之辩解,以游无穷者,彼且恶乎待哉? ——<庄子.逍遥游> 翻译:至于遵循宇宙万物的规律,把握“六气”的变化,遨游于无穷无尽的境域,他还仰赖什么呢! 2.1 初始Sp ...

  8. Go 语言 HTTP Server 源码分析

    http://www.codeceo.com/go-http-server-code.html

  9. 《深入理解Spark-核心思想与源码分析》(五)第五章任务提交与执行

    即欲捭之贵周,即欲阖之贵密.周密之贵,微而与道相随.---<鬼谷子> 解释:译文:如果要分析问题,关键在于周详,如果要综合归纳问题,关键在于严密.周详严密的关键在于精深而与道相随. 解词: ...

  10. 《深入理解Spark-核心思想与源码分析》(三)第三章SparkContext的初始化

    3.1 SparkContext概述 SparkConf负责配置参数,主要通过ConcurrentHaspMap来维护各种Spark的配置属性. class SparkConf(loadDefault ...

随机推荐

  1. [转帖]oracle rac后台进程和LMS说明

    本文摘抄录oracle官方文档,oracle rac使用的后台进程,用以备忘,记录之. About Oracle RAC Background Processes The GCS and GES pr ...

  2. [转帖]tikv性能参数调优

    https://www.cnblogs.com/FengGeBlog/p/10278368.html#:~:text=max-%20bytes%20-for-level-%20base%20%3D%2 ...

  3. [转帖]修改jmeter内存配置(win&mac&linux)

    目录 一.背景: 二.win环境下修改jmeter内存 三.mac&linux环境下修改jmeter内存 四.验证内存是否修改成功 一.背景: 在进行大数据.高并发压测的过程性,有时会遇上JM ...

  4. [转帖]VCSA证书过期问题处理

    1.  故障现象 2022年10月25日,登陆VC报错. 按照报错信息,结合官方文档,判断为STS证书过期导致. vCenter Server Appliance (VCSA) 6.5.x, 6.7. ...

  5. [转帖]iptables命令详解和举例(完整版)

    1.防火墙概述 防火墙,其实说白了讲,就是用于实现Linux下访问控制的功能的,它分为硬件的或者软件的防火墙两种.无论是在哪个网络中,防火墙工作的地方一定是在网络的边缘.而我们的任务就是需要去定义到底 ...

  6. [转帖]linux 系统级性能分析工具 perf 的介绍与使用

    目录 1. 背景知识 1.1 tracepoints 1.2 硬件特性之cache 2. 主要关注点 3. perf的使用 3.0 perf引入的overhead 3.1 perf list 3.2 ...

  7. echarts中坐标与标签刻度对齐

    xAxis: { data: ["土地.房屋及建筑物", "遇用设备", "遇用设备", "裤子", "家具. ...

  8. TienChin 渠道管理-渠道导入

    ChannelController @PostMapping("/importTemplate") void importTemplate(HttpServletResponse ...

  9. 阿里天池实验室简明教程以及Docker安装使用[一]

    1.天池notebook简介和使用 天池实验室是基于PAI DSW探索版开发的,PAI DSW (Data Science Workshop)是为算法开发者量身打造的云天池实验室是基于PAI DSW探 ...

  10. C#合成图片显示不全

    现象 前段时间用C#写了一个小工具,主要功能是随机读取图片并合成一张长图.最近在使用的时候发现个问题,原图片宽度是1080,合成后的图片宽度也是1080,但是合成后的图片没有显示全,就像下面这样 原图 ...