分享組件:go-kratos-ecosystem/components
注:該功能計劃在 v2.11.0 版本中發佈。
原文:https://flc.io/go-is-type/
引言
我們在早期使用 Go 做類型斷言的時候,大多是這麼用:
var v interface{}
v = 10
if _, ok := v.(int); ok {
fmt.Println("v is int")
}
那現在,我們只需要這麼用:
if IsType[int](v) {
fmt.Println("v is int")
}
主角
這就是今天要給大家分享的一個新方法:IsType。這個想法來自於 關於增加 is[T any](any) bool 的提案 。
我們先看看實際的代碼和單測效果。
-
代碼:
func IsType[T any](value any) bool { _, ok := value.(T) return ok } -
單測:
func TestIsType(t *testing.T) { assert.True(t, IsType[int](10)) assert.False(t, IsType[int](int8(10))) assert.True(t, IsType[string]("foo")) assert.False(t, IsType[string](10)) assert.True(t, IsType[time.Time](time.Now())) assert.False(t, IsType[time.Time](10)) assert.True(t, IsType[foo](foo{})) assert.False(t, IsType[foo](10)) assert.True(t, IsType[*foo](&foo{})) assert.False(t, IsType[foo](&foo{})) assert.False(t, IsType[*foo](nil)) assert.True(t, IsType[testInterface](testStruct{})) assert.True(t, IsType[interface{}](testStruct{})) assert.True(t, IsType[any](testStruct{})) assert.True(t, IsType[error](errors.New("foo"))) assert.False(t, IsType[error](nil)) } -
單測結果:
=== RUN TestIsType --- PASS: TestIsType (0.00s) PASS
底層其實就是應用了 Go 1.18+ 中的泛型特性,通過這種方式,我們可以很方便地判斷一個值的類型。原來三行代碼,現在只需要一行代碼就可以搞定。
// 之前
var isType bool
if _, ok := v.(int); ok {
isType = true
}
// 但現在
isType = IsType[int](v)
性能
考慮到使用了泛型,我們來比較下使用原生和使用泛型的性能損耗。
相關腳本:https://github.com/go-kratos-ecosystem/components/pull/185/files
-
原生:
單測腳本:
func testIsType(value any) bool { _, ok := value.(int) return ok } func BenchmarkIsType_Native(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { testIsType(10) } }) }單測結果:
BenchmarkIsType_Native BenchmarkIsType_Native-8 1000000000 0.2330 ns/op -
泛型:
單測腳本:
func BenchmarkIsType_Generics(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { IsType[int](10) } }) }單測結果:
BenchmarkIsType_Generics BenchmarkIsType_Generics-8 1000000000 0.2403 ns/op
可以看到,性能損耗幾乎可以忽略不計。
應用
實際應用中,類似很多語言的 instanceof 操作,我們可以使用這種方式來判斷類型。
type Named interface {
Name() string
}
type Person struct {
Name string
}
func (p Person) Name() string {
return p.Name
}
func main() {
var n Named = Person{"foo"}
if IsType[Person](n) {
fmt.Println(n.Name())
}
}
參考資料
- 關於增加
is[T any](any) bool的提案 - Go 1.18 泛型
- Go Kratos Components
values.IsType- 性能測試腳本
- PR: Add IsType