首頁 > 軟體

Golang泛型與反射的應用詳解

2022-06-15 10:00:14

1. 泛型

1.1 定義

  • 泛型生命週期只在編譯期,旨在為程式設計師生成程式碼,減少重複程式碼的編寫
  • 在比較兩個數的大小時,沒有泛型的時候,僅僅只是傳入型別不一樣,我們就要再寫一份一模一樣的函數,如果有了泛型就可以減少這類程式碼

1.2 例子

// SumInts 將map的值相加,如果需要新增的資料型別不同,那麼就需要定義兩個
func SumInts(m map[string]int64) int64 {
    var s int64
    for _, v := range m {
        s += v
    }
    return s
}
func SumFloats(m map[string]float64) float64 {
    var s float64
    for _, v := range m {
        s += v
    }
    return s
}

如果使用泛型的話只需要定義泛型方法即可(如果報一下編譯錯誤的話,是idea版本過低,升級版本即可,但是執行沒有問題)

func main() {
	ints := make(map[string]int64, 5)
	ints["name"] = 5
	ints["value"] = 6
	floats := make(map[string]float64, 5)
	floats["name"] = 5.6
	floats["value"] = 6.5
	fmt.Printf("Gnneric sums: %v and %vn", 
		SumIntsOrFloats[string, int64](ints), 
		SumIntsOrFloats[string, float64](floats))
    //可以將型別刪除
    fmt.Printf("Gnneric sums: %v and %vn", 
		SumIntsOrFloats(ints), 
		SumIntsOrFloats(floats))
}
//SumIntsOrFloats 定義泛型方法
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
	var s V
	for _, v := range m {
        s += v
    }
	return s
}

1.3 自定義泛型型別

  • any:代表 go裡面所有的內建型別,等價於 interface {}
  • comparable:代表go裡面內建的可比較型別:int、uint、float、bool、struct、指標等一切可比較型別
  • ~ 符號:用來表示改型別的衍生型別
//型別約束
type Number interface {
	int64 | float64
}
//進行型別約束時就可以使用當前類
func SumIntsNumbers[K comparable, V Number](m map[K]V) V {
	var s V
	for _, v := range m {
        s += v
	}
	return s
}

1.4 泛型與switch結合使用

func main() {
	fmt.Println(Get(12))
}
//go中不能直接將泛型與switch使用
func Get[T any](t T) T {
	var ti interface{} = &t
	switch v := ti.(type) {
	case *int:
		*v = 18
	}
	return t
}

1.5 泛型實戰

使用泛型定義Json解析方法

//根據傳入的型別通過反射獲取到型別以及值
func typeFunc[E any](v any, e *E) *E {
	valueOf := reflect.ValueOf(v)
	typeOf := reflect.TypeOf(v)
	if k := typeOf.Kind(); k == reflect.Slice {
		json.Unmarshal(valueOf.Bytes(), e)
	}
	return e
}
func main() {
    user1 := &User{}
	user1 = typeFunc[User](marshal, user1)
	fmt.Printf("%+v", user1)
}

2. 反射

2.1 定義

Golang提供了一種機制,在編譯時不知道型別的情況下,可更新變數、執行時檢視值、呼叫方法以及直接對他們的佈局進行操作的機制,稱為反射。

2.2 方法

方法說明返回
reflect.ValueOf()獲取輸入引數介面中的資料的值,如果未空則返回 0,注意當前方法會使物件逃逸到堆空間當中返回的是 Value 物件
reflect.TypeOf()動態獲取輸入引數介面中的值的型別,如果為空則返回 nil返回的是 Type 物件

Value

type Value struct {
	typ *rtype
    //儲存型別的值
	ptr unsafe.Pointer
    //指標型別
	flag
    //獲取到值的指向地址,用於通過反射修改值
    Elem() Type
    //給value設定值
    Set()
}

Type

type Type interface {
    //根據索引獲取到方法
	Method(int) Method
    //通過名稱獲取到方法
	MethodByName(string) (Method, bool)
    //獲取到方法的數量
	NumMethod() int
    //獲取結構名稱
	Name() string
    //獲取包路徑
	PkgPath() string
    //獲取到當前型別
	Kind() Kind
    //判斷當前型別是否實現了介面
	Implements(u Type) bool
    //以位為單位返回型別的x
	Bits() int
    //獲取到屬性值的型別,型別必須是:Array、Chan、Map、Pointer、Slice,否則報錯
	Elem() Type
    //獲取到指定所以的值
	Field(i int) StructField
    //獲取到對應索引的巢狀欄位
	FieldByIndex(index []int) StructField
	//通過名稱獲取到對應的欄位
	FieldByName(name string) (StructField, bool)
	FieldByNameFunc(match func(string) bool) (StructField, bool)
    .....
}

2.3 反射讀取

func stringReflect() {
	name := "這是第一個反射字串"
	valueOf := reflect.ValueOf(name)
	typeOf := reflect.TypeOf(name)
	fmt.Println(valueOf)
	fmt.Println(typeOf)
}

type Name struct {
	Name string
	Age string `use:"Ok"`
}
func (n Name) Show()  {
	fmt.Println(n.Name)
}
func structReflect() {
	name := Name{
		Name: "這是反射結構",
	}
	valueOf := reflect.ValueOf(name)
	typeOf := reflect.TypeOf(name)
	fmt.Printf("value值:%+vn", valueOf)
	fmt.Printf("型別名稱:%sn", typeOf.Name())
	methodNum := typeOf.NumMethod()
	fmt.Printf("獲取到方法的數量:%d", methodNum)
	for i := 0; i < methodNum; i++ {
		method := typeOf.Method(i)
		fmt.Printf("%vt", method.Name)
	}
	fmt.Println()
	methodByName, _ := typeOf.MethodByName("Show")
	fmt.Printf("根據Show查詢指定方法:%vn", methodByName)
	//判斷是否實現了當前介面,因為介面型別不能建立範例,所以把 nil 強制轉為 *IName 型別
	implements := typeOf.Implements(reflect.TypeOf((*IName)(nil)).Elem())
	fmt.Printf("當前型別:%s,是否實現介面:%s,%vn", typeOf.Name(), "IName", implements)
	fieldNum := typeOf.NumField()
	for i := 0; i < fieldNum; i++ {
		field := typeOf.Field(i)
		fmt.Printf("欄位名稱:%vt", field.Name)
		if lookup, ok := field.Tag.Lookup("use") ; ok {
			fmt.Printf("獲取標籤:%v", lookup)
		}
	}
}

2.4 反射操作

func setValue() {
	name := Name{
		Name: "這是反射結構",
	}
	valueOf := reflect.ValueOf(&name)
	fmt.Printf("設定值之前:%+vn", valueOf)
	//獲取到地址值
	valueOf = valueOf.Elem()
	name1 := Name{
		Name: "這是通過反射設定的值",
	}
	valueOf.Set(reflect.ValueOf(name1))
	fmt.Printf("設定值之後:%+vn", valueOf)
	//修改欄位值
	fieldValueOf := valueOf.FieldByName("Name")
	fieldValueOf.SetString("這是修改欄位之後的值")
	fmt.Printf("修改欄位之後:%vn", name.Name)
	//呼叫方法
	methodByName := valueOf.MethodByName("Show")
	values := make([]reflect.Value, 0)
	methodByName.Call(values)
}

2.5 判斷

func judgeType() {
	name := Name{Name: "123"}
	typeOf := reflect.TypeOf(name)
	switch typeOf.Kind() {
	case reflect.Slice:
		fmt.Println("切面")
	case reflect.Array:
		fmt.Println("陣列")
	case reflect.Struct:
		fmt.Println("結構體")
	}
    //判斷具體型別
	var ti interface{} = &name
	switch ti.(type) {
	case *Name:
		fmt.Printf("%+vn", ti)
	}
}

到此這篇關於Golang泛型與反射的應用詳解的文章就介紹到這了,更多相關Golang泛型與反射內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com