Golang 反射機制|Go主題月

由於沒有強類型語言的經驗,反射這個概念,以前確實沒怎麼接觸過。在維基百科上搜了一下,具體解釋以下:編程

在計算機學中,反射式編程(英語:reflective programming)或反射(英語:reflection),是指計算機程序在運行時(runtime)能夠訪問、檢測和修改它自己狀態或行爲的一種能力。用比喻來講,反射就是程序在運行的時候可以「觀察」而且修改本身的行爲。markdown

go 中的反射也是這種做用,能夠在程序運行期間,獲取變量的類型與值的信息,而後進行訪問或或者修改。go 語言中,內置了 reflect 包,用來獲取一個變量的類型(type)與值(value),對應的方法分別爲 reflect.TypeOf()reflect.ValueOf()函數

反射類型

TypeOf 方法,會返回該變量的類型對象,類型對象下能夠獲取到變量的類型與種類。spa

import (
	"fmt"
	"reflect"
)

func main() {
	// 定義一個int類型的變量
	var i int = 1
	// 獲取變量的類型對象
	var typeOfNum = reflect.TypeOf(i) 

  // 輸出類型與種類
  typeOfNumName = typeOfNum.Name()
  typeOfNumKind = typeOfNum.Kind()
  fmt.Printf("name: %s, kind: %s", typeOfNumName, typeOfNumKind)
}
複製代碼

能夠看到,此時的類型與種類都爲 int指針

類型與種類

類型表示定義變量的時候指定的類型,能夠反映 type 關鍵字定義的類型,而種類是變量最終歸屬的類型。提及來可能比較蒼白,咱們直接上代碼。code

type num int

// 定義一個num類型的變量
var i num = 1
var typeOfNum = reflect.TypeOf(i) 
複製代碼

能夠看到,此時的類型爲 num,種類爲 intorm

對於一些引用類型的變量,好比切片、函數、結構體,kind 都能準確反映其底層的類型。對象

func printTypeOf(typeOf reflect.Type) {
	fmt.Printf("name: %s, kind: %s\n", typeOf.Name(), typeOf.Kind())
}

type Person struct {}
type IntSlice []int
func main() {
	var a = IntSlice{}
	var b = Person{}
	printTypeOf(reflect.TypeOf(a))
	printTypeOf(reflect.TypeOf(b))
}
複製代碼

而面對匿名結構體或者匿名函數,其類型值會返回爲空。索引

func main() {
	var a = struct {}{}
	printTypeOf(reflect.TypeOf(a))
}
複製代碼

反射值

ValueOf 方法,能夠獲取一個變量的值。string

var i = 3.1415926
var s = "歡迎關注個人公衆號:『天然醒的筆記本』"

fmt.Println(reflect.ValueOf(s))
fmt.Println(reflect.ValueOf(i))
複製代碼

經過反射的值對象,也能取到變量的種類,而且還能根據其種類,調用對應的方法獲取變量的真實值。

var i = 100
var v = reflect.ValueOf(i)

fmt.Println(v.Int()) // 若是值是 Int 類型,能夠經過 Int 方法獲取具體值
fmt.Println(v.Kind())
複製代碼

修改值

經過反射獲得的值對象,能夠對變量自己的值進行修改。首先,在獲取反射值時,不能直接獲取變量的反射值,而是要先取其指針的值對象。

var i = 100
var v = reflect.ValueOf(&i) // 取出變量i的指針的值對象

fmt.Println(v.Kind(), v)
複製代碼

取出指針的值對象以後,不能當即賦值,由於此時拿到的是變量的地址。

要賦值的話,須要先調用 Elem 方法,取出具體元素,而後進行賦值。

var i = 100
var v = reflect.ValueOf(&i) // 取出變量i的指針的值對象

var e = v.Elem()
e.SetInt(500) // 修改元素值

fmt.Println(e.Kind(), i)
複製代碼

值對象與結構體

前面介紹過,經過反射能夠獲得變量的值,對於結構體來講,也是同樣。

type Person struct {
	name string
	age int
	gender string
	address string
}

var p = Person{"Shenfq", 25, "男", "湖南長沙"}
var v = reflect.ValueOf(p)

fmt.Println(v.Kind(), v)
複製代碼

反射值對象還提供了一些方法,專門用來針對結構體成員的信息獲取。

NumField()

NumField() 能夠獲取結構體成員的具體數量。

var p = Person{"Shenfq", 25, "男", "湖南長沙"}
var v = reflect.ValueOf(p)

fmt.Println("Person 結構體成員數:", v.NumField())
複製代碼

Field()

Field() 能夠獲取結構體指定索引位置的成員的反射值。

var p = Person{"Shenfq", 25, "男", "湖南長沙"}
var v = reflect.ValueOf(p)
var num = v.NumField()
for i :=0; i < num; i++ {
  var val = v.Field(i)
  fmt.Printf("Person[%d]: %s %v\n", i, val.Type(), val)
}
複製代碼

FieldByName()

FieldByName() 能夠獲取結構體指定成員名稱的成員的反射值。

var p = Person{"Shenfq", 25, "男", "湖南長沙"}
var v = reflect.ValueOf(p)
var vOfName = v.FieldByName("name")
fmt.Printf("Person[name]: %s %v\n", vOfName.Type(), vOfName)
複製代碼

相關文章
相關標籤/搜索