一、Golang中的反射基本使用


  Go中也提供了反射機制,與Java一樣Go的反射也是在運行時獲取對象的相關信息,更新對象內部狀態;Golang通過反射可以獲取對象類型、字段類型與值、調用struct實例方法、更新實例值等;
  Go關於反射相關的對象、函數都在reflect包中最主要的兩個為:Type與Value;
  Go提供了下面兩個函數,這兩個是Go反射的核心;
  reflect.TypeOf 返回目標對象的類型
  reflect.ValueOf 返回值目標對象的值

t:=1
fmt.Println(reflect.TypeOf(t), reflect.ValueOf(t))
輸出:int 1

通過反射操作Struct

type Demo struct {
  Id   int
  Name string
}
func (d *Demo) Back() {
    fmt.Println("調用方法 Back")
}
func (d *Demo) Add(a, b int) int {
	return a + b
}

獲取結構體中每個成員變量的名稱與值:

 d := &Demo{Id: 2, Name: "test"}
 getValue(d)
 輸出: Id :  2
      Name :  test

func getValue(v interface{}) {
	t := reflect.TypeOf(v)
	o := reflect.ValueOf(v)
	if t.Kind() == reflect.Ptr {
		t = t.Elem()   //獲取類型指針中的類型
	}
	if o.Kind() == reflect.Ptr {
		o = o.Elem()  //獲取值地址中的值
	}
	num := t.NumField()  //獲取字段個數
	for i := 0; i < num; i++ {
		f := o.Field(i)     //獲取字段的值
		fieldName := t.Field(i).Name  //獲取字段名稱
		switch f.Kind() {
		case reflect.Int:
			fmt.Println(fieldName, ": ", f.Int()) 
		case reflect.String:
			fmt.Println(fieldName, ": ", f.String())
		default:
			fmt.Println("類型不不支持")
		}
	}
}

  對於引用類型使用reflect.TypeOf返回的是該類型的指針,reflect.ValueOf返回的是該類型的值地址;所以對於引用類型都要的相關操作都要調用Elem()函數獲取真實的類型與值;
  調用Type或Value對象的NumField函數獲取結構體的字段個數
  調用Value對象的Field(i) 可獲取字段的值;
  調用Value對象的Kind()函數可獲取字段的類型;
  該Value對應於哪種類型調用對應的函數即可獲取得到相應的值,如類型不一致將拋出:panic: reflect: call of reflect.Value.Xxx on int Value;

修改結構體中每個成員變量的值:

    d := new(Demo)
    setValue(d)
    fmt.Println(d)
    輸出:&{88 Test}

    func setValue(v interface{}) {
	t := reflect.TypeOf(v)
	o := reflect.ValueOf(v)
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	if o.Kind() == reflect.Ptr {
		o = o.Elem()
	}
	num := t.NumField()
	for i := 0; i < num; i++ {
		f := o.Field(i)
		switch f.Kind() {
		case reflect.Int:
			f.SetInt(88)    //往該字段設值
		case reflect.String:
			f.SetString("Test") /往該字段設值
		default:
			fmt.Println("類型不支持")
		}
	}
    }

  修改字段值與獲取值一樣,類型一定要一致,如不一致將拋異常,如int類型卻調用SetString設值:panic: reflect: call of reflect.Value.SetString on int Value;

調用結構體的無參方法:

d := new(Demo)
callMethod(d) 
輸出:調用方法 Back

func callMethod(v interface{}) {
	o := reflect.ValueOf(v)
	o.MethodByName("Back").Call(nil)
}

  調用MethodByName根據名稱獲取方法,Call調用該方法;

調用結構體的有參方法:

d := new(Demo)
callMethodParam (d) 
輸出:3

func callMethodParam(p interface{}) {
	o := reflect.ValueOf(p)
	args:=[]reflect.Value{reflect.ValueOf(1), reflect.ValueOf(2)}
	v:= o.MethodByName("Add").Call(args)
	fmt.Println(v[0])
}

  比較常用的方法還有:
  使用名字獲取結構體的成員
Refletct.ValueOf(*e).FieldByName(“Name”)
  獲取結構體成員的json標記信息
reflect.TypeOf(s) . Field(0).Tag.Get(“key”)

  Golang的反射也遵循Go語言規則,反射無法修改結構體中的私有對象,無法調用私有私有方法,可訪問私有成員,修改私有成員將會拋出reflect.flag.mustBeAssignable異常;

文章首發地址:Solinx
https://mp.weixin.qq.com/s/W0UVbFxMMeXA5HuNNZlK-A


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM