golang中的reflect包用法


最近在寫一個自動生成api文檔的功能,用到了reflect包來給結構體賦值,給空數組新增一個元素,這樣只要定義一個input結構體和一個output的結構體,並填寫一些相關tag信息,就能使用程序來生成輸入和輸出的相關文檔。

介紹

reflect包是golang中很重要的一個包,實現了在運行時允許程序操縱任意類型對象的功能。可以看下文檔簡單了解一下。

在reflect中,最重要的是Value類,只有先獲取到一個對象或者變量的Value對象后,我們才可以對這個對象或者變量進行更進一步的分析和處理。我們可以使用reflect.ValueOf()方法獲取Value對象。


var i int
value := reflect.ValueOf(i) // 使用ValueOf()獲取到變量的Value對象

type S struct {
    a string
}

var s S
value2 := reflect.ValueOf(s) // 使用ValueOf()獲取到結構體的Value對象

獲取到對象或者變量的Value對象后,我們就可以對他們進一步的操作了。

1.獲取對象或者變量的類型(Value.Type()和Value.Kind())

Value.Type()Value.Kind()這兩個方法都可以獲取對象或者變量的類型,如果是變量的話,使用這兩個方法獲取到的類型都是一樣,差別是結構體對象,舉個例子看一下:

var i int
value := reflect.ValueOf(i)

log.Println(value.Type()) //輸出:int
log.Println(value.Kind()) //輸出:int

type S struct {
    a string
}

var s S
value2 := reflect.ValueOf(s) // 使用ValueOf()獲取到結構體的Value對象


log.Println(value2.Type()) //輸出:S
log.Println(value2.Kind()) //輸出:struct

變量i使用kind和type兩個方法都輸出了int,而結構體s的Type()方法輸出了S,Kind()方法輸出了struct,由此可以總結如下,如果你想拿到結構體里面定義的變量信息的時候,使用Type(f)方法。如果只是相判斷是否是結構體時,就使用Kind()

2.獲取變量的值和給變量賦值

獲取變量的值使用value.Interface()方法,該方法會返回一個value的值,不過類型是interface。給變量賦值需要先判斷該變量的類型,使用之前提到過的Value.Kind()方法,如果變量的類型是reflect.Int,我們就可以使用Value.SetInt()方法給變量賦值。下面是一個例子:

var i int = 1

// 獲取Value,這里注意,如果你要改變這個變量的話,需要傳遞變量的地址
value := reflect.ValueOf(&i)

// value是一個指針,這里獲取了該指針指向的值,相當於value.Elem()
value = reflect.Indirect(value)

// Interface是獲取該value的值,返回的是一個interface對象
log.Println(value.Interface()) // 輸出:1

// 把變量i的值設為2
if value.Kind() == reflect.Int {
	value.SetInt(2)
}

log.Println(value.Interface()) // 輸出:2

給結構體對象中的成員變量賦值的方法:

type S struct {
	A string // 注意:只有大寫開頭的成員變量可以Set
}

s := S{"x"}

value := reflect.ValueOf(&s)

value = reflect.Indirect(value)


//value是結構體s,所以打印出來的是整個結構體的信息
log.Println(value.Interface()) //輸出: {x}

f0 := value.FieldByName("A") //獲取結構體s中第一個元素a

log.Println(f0) // 輸出: x

if f0.Kind() == reflect.String {
	if f0.CanSet() {
		f0.SetString("y")
	}
}

log.Println(f0) // 輸出: y

log.Println(value.Interface()) //輸出: {y}

結構體這里需要注意的是,只有公有的成員變量可以被reflect改變值,私有的變量是無法改變值得。

3.獲取結構體成員變量的tag信息

由於golang變量大小寫和公有私有息息相關,所以碼農門很難按照自己的意願來定義變量名。於是golang提供了tag機制,來給變量提供一個標簽,這個標簽可以作為一個別名,來給一些存儲結構來獲取結構體變量名字使用。下面是一個獲取結構體成員變量tag信息的例子:

type S struct {
	A string `json:"tag_a"`
}

s := S{}

value := reflect.ValueOf(&s)

value = reflect.Indirect(value)

//獲取結構體s的類型S
vt := value.Type()

//獲取S中的A成員變量
f, _ := vt.FieldByName("A")

//獲取成員變量A的db標簽
log.Println(f.Tag.Get("json")) //輸出: tag_a

未完待續。。。


免責聲明!

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



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