ソースコードと合わせて理解するインターフェース{}
2883 ワード
まずgoのinterfaceを明確にして、二つに分けて、方法のない声明と方法のある声明の、対応ソースの定義は以下の通りです.
参考記事:http://legendtkl.com/2017/07/01/golang-interface-implement/ https://www.tapirgames.com/blog/golang-interface-implementation
//
type iface struct {
tab *itab
data unsafe.Pointer
}
//
type eface struct {
_type *_type
data unsafe.Pointer
}
そのうちdataは実際の値情報を指し、_typeは内部タイプ情報を定義するデータ構造であり、itabにはインターフェースタイプ、実際タイプ、実現方法セットなどのインターフェース関連情報が定義されています.type itab struct {
inter *interfacetype
_type *_type
link *itab
hash uint32 // copy of _type.hash. Used for type switches.
bad bool // type does not implement interface
inhash bool // has this itab been added to hash?
unused [2]byte
fun [1]uintptr // variable sized
}
interfacetypeは、インターフェース自体に関する情報であり、このインターフェースのタイプ、含まれる方法セット、およびパッケージ名などを含む.ifeceは、一つのstructをinterfaceに変えた後の情報に対応します.funにはinterfacetypeが必要な方法セットの具体的な実装が格納されています.方法メモリには順番に保存されていますので、funには最初の方法アドレスのポインタだけが必要です.hashSize = 1009
hash [hashSize]*itab
性能を考慮するためかもしれませんが、すべてのitabは実際にグローバルhashテーブルに保存されています.interfaceのタイプを別のタイプのinterfaceに変えたとき、呼び出しのソースコードは次の通りです.func convI2I(inter *interfacetype, i iface) (r iface) {
tab := i.tab
if tab == nil {
return
}
if tab.inter == inter {
r.tab = tab
r.data = i.data
return
}
r.tab = getitab(inter, tab._type, false)
r.data = i.data
return
}
i中tabのinterと転送するinterが同じであれば、直接に変換できます.そうでなければ、getitabを呼び出して、転送すべきrのitabを取得します.func getitab(inter *interfacetype, typ *_type, canfail bool) *itab {
...
h := itabhash(inter, typ)
var m *itab
var locked int
for locked = 0; locked < 2; locked++ {
if locked != 0 {
lock(&ifaceLock)
}
for m = (*itab)(atomic.Loadp(unsafe.Pointer(&hash[h]))); m != nil; m = m.link {
if m.inter == inter && m._type == typ {
...
return m
}
}
m = (*itab)(persistentalloc(unsafe.Sizeof(itab{})+uintptr(len(inter.mhdr)-1)*sys.PtrSize, 0, &memstats.other_sys))
m.inter = inter
m._type = typ
additab(m, true, canfail)
unlock(&ifaceLock)
if m.bad {
return nil
}
return m
上のコードは、まずinterとtypでhash値を計算し、グローバルitabのhashテーブルにこのhash値のitabが含まれているかどうかを確認し、このitabのinterとtypが条件に該当する場合、以前にも同様の変換があり、タイプ変換条件に適合していることを証明します.もしないなら、additabを呼ぼうとします.func additab(m *itab, locked, canfail bool) {
inter := m.inter
typ := m._type
...
h := itabhash(inter, typ)
m.link = hash[h]
m.inhash = true
atomicstorep(unsafe.Pointer(&hash[h]), unsafe.Pointer(m))
}
省略記号の部分はmのtypがinterメソッドセットの論理を実現しているかどうかを判断します.全部実現していないと、証明タイプの変換が合法的でないm.badはtrueに置かれます.変換が適法であれば、最後にmをグローバルitab hashテーブルに保存します.参考記事: