【GO】一篇文章帶你看透反射的原理

反射是什麼

  • 對於運行時內存中的任何一個對象,你不須要預先知道其類型是什麼,也能訪問其所有屬性,調用其所有方法;
  • 反射的主要做用在於編寫通用的框架;
  • 用靜態類型interface{}保存一個值,經過調用TypeOf獲取其動態類型信息,該函數返回一個Type類型值。調用ValueOf函數返回一個Value類型值,該值表明運行時的數據。Zero接受一個Type類型參數並返回一個表明該類型零值的Value類型值。

反射應用場景舉例:導出商品列表到Excel

  • 需求是:無論用戶在界面上看到什麼商品,當它點擊一下導出按鈕,就將該商品的全部屬性和值寫出爲文件;
  • 本例的難點是:咱們沒法預知用戶會選擇導出什麼類型的商品數據、它有哪些屬性,也就沒法相應地去建立Excel數據表的列;
  • 由於商品的種類太多,若是用「正射」去作,那麼有多少商品類型咱們就要寫多少個switch或if分支,而後在每個分支里根據當前分支的具體商品類型去構造相應的數據列,這顯然是狠蹩腳、狠難維護和擴展的;
  • 而經過反射來作就易如反掌了,管你用戶要導出的是什麼商品實例,我能夠動態地解析其類型、動態獲知其全部屬性和方法,而後根據再根據其具體屬性名稱去建立相應的表格列,並將屬性值填入其中;

而後咱們開始看案例

首先須要定義倆個結構體

而且People繼承PeopleParent微信

package main

import (
	"fmt"
	"reflect"
)

type PeopleParent struct { 
	Kaka string
}

type People struct { 
	PeopleParent
	Name string
	Age  int
}

定義一個方法爲了在使用value接口演示使用

func (p People) Eat(name string) { 
	fmt.Println("咔咔在吃什麼呢!", name)
	p.Name = name
}

在main函數裏邊咱們把People結構體的對象給建立出來

建立了機構對象而且複製給P 而且調用了typeAPI方法,把p傳入進去框架

func main() { 
	p := People{ 
		PeopleParent: PeopleParent{ Kaka: "咔咔的父類屬性"},
		Name:         "咔咔",
		Age:          24,
	}
	typeAPI(p)
	valueAPI(p)
}

開始咱們的typeAPI的一些接口

func typeAPI(obj interface{ }) { 
	// 返回保存值的類型
	oType := reflect.TypeOf(obj)
	fmt.Println(oType) // main.People
	// 原始類型
	kind := oType.Kind()
	fmt.Println(kind) // struct
	// 類型名稱
	fmt.Println(oType.Name()) // People
	// 屬性和方法的個數
	fmt.Println(oType.NumField())  // 2
	fmt.Println(oType.NumMethod()) // 0
	// 獲取所有屬性
	for i := 0; i < oType.NumField(); i++ { 
		structField := oType.Field(i)
		// name string age int
		fmt.Println(structField.Name, structField.Type)
	}
	// 獲取所有方法
	for i := 0; i < oType.NumMethod(); i++ { 
		structMethod := oType.Method(i)
		fmt.Println(structMethod.Name, structMethod.Type)
	}
	// 獲取父類的屬性 []int{0, 0}獲取第0個父類 第0個屬性
	fmt.Println(oType.FieldByIndex([]int{ 0, 0}).Name)
}

value的一些接口

在這裏咱們須要注意的是方法傳入的是結構體的值並不是指針函數

在修改屬性值時是傳的指針,這點看清楚spa

咱們看到的是elem也能夠調用結構體的方法指針

咱們代碼的一開始是ValueOf§ 若是改成*p也是跟elem同樣的code

valueOf := reflect.ValueOf(&p)
	byEat := valueOf.MethodByName("Eat")
	byEat.Call([]reflect.Value{ reflect.ValueOf("西瓜")})
	fmt.Println(valueOf)
func valueAPI(p People) { 
	valueOf := reflect.ValueOf(p)
	// 獲取全部屬性值
	for i := 0; i < valueOf.NumField(); i++ { 
		value := valueOf.Field(i)
		// {}
		//咔咔
		//24
		fmt.Println(value)
	}
	// 獲取父類屬性
	fieldByIndex := valueOf.FieldByIndex([]int{ 0, 0})
	fmt.Println(fieldByIndex.Interface()) // 咔咔的父類屬性

	// 得到指針value的內容進而得到成員的值
	valuePrt := reflect.ValueOf(&p)
	elem := valuePrt.Elem()
	value := elem.Field(0).Interface()
	fmt.Println(value) //{咔咔的父類屬性}

	// 根據屬性名獲取值
	age := elem.FieldByName("Age")
	fmt.Println("咔咔的年齡", age) // 咔咔的年齡 24
	// 修改屬性值
	elem.FieldByName("Age").SetInt(26)
	fmt.Println(elem) //{ {咔咔的父類屬性} 咔咔 26}

	// 調用對象的方法
	mValue := elem.MethodByName("Eat")
	// 參數須要反射
	mValue.Call([]reflect.Value{ reflect.ValueOf("西瓜")})
	fmt.Println(elem) //咔咔在吃什麼呢! 西瓜
}

完整代碼

package main

import (
	"fmt"
	"reflect"
)

type PeopleParent struct { 
	Kaka string
}

type People struct { 
	PeopleParent
	Name string
	Age  int
}

func (p People) Eat(name string) { 
	fmt.Println("咔咔在吃什麼呢!", name)
	p.Name = name
}

func main() { 
	p := People{ 
		PeopleParent: PeopleParent{ Kaka: "咔咔的父類屬性"},
		Name:         "咔咔",
		Age:          24,
	}
	typeAPI(p)
	valueAPI(p)
}
func typeAPI(obj interface{ }) { 
	// 返回保存值的類型
	oType := reflect.TypeOf(obj)
	fmt.Println(oType) // main.People
	// 原始類型
	kind := oType.Kind()
	fmt.Println(kind) // struct
	// 類型名稱
	fmt.Println(oType.Name()) // People
	// 屬性和方法的個數
	fmt.Println(oType.NumField())  // 2
	fmt.Println(oType.NumMethod()) // 0
	// 獲取所有屬性
	for i := 0; i < oType.NumField(); i++ { 
		structField := oType.Field(i)
		// name string age int
		fmt.Println(structField.Name, structField.Type)
	}
	// 獲取所有方法
	for i := 0; i < oType.NumMethod(); i++ { 
		structMethod := oType.Method(i)
		fmt.Println(structMethod.Name, structMethod.Type)
	}
	// 獲取父類的屬性 []int{0, 0}獲取第0個父類 第0個屬性
	fmt.Println(oType.FieldByIndex([]int{ 0, 0}).Name)
}

func valueAPI(p People) { 
	valueOf := reflect.ValueOf(p)
	//valueOf := reflect.ValueOf(&p)
	//byEat := valueOf.MethodByName("Eat")
	//byEat.Call([]reflect.Value{reflect.ValueOf("西瓜")})
	//fmt.Println(valueOf)
	// 獲取全部屬性值
	for i := 0; i < valueOf.NumField(); i++ { 
		value := valueOf.Field(i)
		// {}
		//咔咔
		//24
		fmt.Println(value)
	}
	// 獲取父類屬性
	fieldByIndex := valueOf.FieldByIndex([]int{ 0, 0})
	fmt.Println(fieldByIndex.Interface()) // 咔咔的父類屬性

	// 得到指針value的內容進而得到成員的值
	valuePrt := reflect.ValueOf(&p)
	elem := valuePrt.Elem()
	value := elem.Field(0).Interface()
	fmt.Println(value) //{咔咔的父類屬性}

	// 根據屬性名獲取值
	age := elem.FieldByName("Age")
	fmt.Println("咔咔的年齡", age) // 咔咔的年齡 24
	// 修改屬性值
	elem.FieldByName("Age").SetInt(26)
	fmt.Println(elem) //{ {咔咔的父類屬性} 咔咔 26}

	// 調用對象的方法
	mValue := elem.MethodByName("Eat")
	// 參數須要反射
	mValue.Call([]reflect.Value{ reflect.ValueOf("西瓜")})
	fmt.Println(elem) //咔咔在吃什麼呢! 西瓜
}

博主微信歡迎交流

在這裏插入圖片描述

相關文章
相關標籤/搜索