我試圖高效地測試一個接口{}是否實現了給定的函數,我的解決方案是僅使用此函數創建接口,然後檢查接口是否實現了此單一函數接口。這兩個選項似乎是使用反射或類型斷言。兩者似乎都有相同的行爲,但速度差異很大。爲什麼反映Type.Implements()比類型斷言慢得多?
查看Value.Implements()的代碼,它會對在該值上定義的函數進行線性掃描,並將其與接口進行比較。 然而,類型斷言似乎做了一個恆定的時間比較(獨立於接口中的函數數量)。
是否有理由爲什麼實現()不只是做一個類型斷言?
基準:
package benchmarks
import (
"reflect"
"testing"
)
type ITest interface {
Foo()
}
type Base struct{}
func (Base) A() {}
func (Base) B() {}
func (Base) C() {}
func (Base) D() {}
func (Base) E() {}
func (Base) F() {}
func (Base) G() {}
func (Base) H() {}
func (Base) I() {}
func (Base) J() {}
var Interface = reflect.TypeOf((*ITest)(nil)).Elem()
func BenchmarkReflection(b *testing.B) {
var iface interface{}
iface = Base{}
for i := 0; i < b.N; i++ {
if reflect.TypeOf(iface).Implements(Interface) {
b.FailNow()
}
}
}
func BenchmarkAssertion(b *testing.B) {
var iface interface{}
iface = Base{}
for i := 0; i < b.N; i++ {
if _, ok := iface.(ITest); ok {
b.FailNow()
}
}
}
結果:在圍棋
go test -run=XXX -bench=. so_test.go
goos: linux
goarch: amd64
BenchmarkReflection-8 10000000 208 ns/op
BenchmarkAssertion-8 200000000 9.24 ns/op
PASS
ok command-line-arguments 5.115s
是否有理由爲什麼實現()不做緩存?它表明它期望某個接口的實現是否可以改變(然而,如果這是真的,那麼類型斷言緩存肯定是不正確的?) – bradleyjkemp
因爲反射速度很慢。 – Volker
@bradleyjkemp,我建議參考「計算Itable」部分[這裏](https://research.swtch.com/interfaces)。我的回答是:即使類型斷言/開關並將具體類型的值賦給接口類型的變量,可能需要在運行時爲該類型計算新的可用屬性,編譯器會假定可能只有一個有限在一個明智的程序中這樣計算的數量 - 僅僅是因爲Go程序在運行時不能改變它自己... – kostix