2018-05-02 Go Reflect

轉載自: Go Reflect

最近在看一些go語言標準庫以及第三方庫的源碼時挪略,發(fā)現(xiàn)go的reflect被大量使用甫匹,雖然反射的機制大多數(shù)語言都支持箕般,但好像都沒有go一樣這么依賴反射的特性瘤泪。個人覺得键痛,reflect使用如此頻繁的一個重要原因離不開go的另一個特性,空接口interface{},reflect配合空接口羊精,讓原本是靜態(tài)類型的go具備了很多動態(tài)類型語言的特征斯够。 另外,雖然反射大大增加了go語言的靈活性喧锦,但要完全掌握它的原理和使用也還是有一點難度的读规。

go的reflect庫有兩個重要的類型:

reflect.Type
reflect.Value
Type,Value分別對應對象的類型和值數(shù)據(jù)

還有兩個重要的函數(shù):

reflect.TypeOf(i interface{}) Type
reflect.TypeOf()返回值的類型就是reflect.Type。

reflect.ValueOf(i interface{}) Value
reflect.ValueIOf()返回值的類型就是reflect.Value

reflect.Type
reflect.TypeOf(i interface{}) Type

因為reflect.Typeof的參數(shù)是空接口類型燃少,因此可以接收任意類型的數(shù)據(jù)束亏。 TypeOf()的返回值是這個接口類型對應的reflect.Type對象。通過Type提供的一些方法阵具,就可以獲得這個接口實際的靜態(tài)類型枪汪。

import (
  "fmt"
  "reflect"
)

type Foo struct {
  X string
  Y int
}

func main() {
  var i int = 123
  var f float32 = 1.23
  var l []string = []string{"a", "b", "c"}

  fmt.Println(reflect.TypeOf(i))    //int
  fmt.Println(reflect.TypeOf(f))    //float32
  fmt.Println(reflect.TypeOf(l))    //[]string

  var foo Foo
  fmt.Println(reflect.TypeOf(foo))    //main.Foo

}

查看reflect包的源代碼可以看到,reflect.Type的定義如下:

type Type interface {
  Align() int
  FieldAlign() int
  Method(int) Method
  MethodByName(string) (Method, bool)
  NumMethod() int
  Name() string
  String() string
  Elem() Type
  Field(i int) StructField
  FieldByName(name string) (StructField, bool)
  Len() int
  .....
}

可見reflect.Type是一個接口類型的對象怔昨,這個接口包含了很多方法雀久,像Name(),Field(),Method()等,下面再通過實例來了解幾個比較重要的方法趁舀。

type Foo struct {
  X string
  Y int
}

func (f Foo) do() {
  fmt.Printf("X is: %s, Y is: %d", f.X, f.Y)

}

func main() {
  var s string = "abc"
  fmt.Println(reflect.TypeOf(s).String()) //string
  fmt.Println(reflect.TypeOf(s).Name())   //string

  var f Foo
  typ := reflect.TypeOf(f)
  fmt.Println(typ.String()) //main.Foo
  fmt.Println(typ.Name())     //Foo 赖捌,返回結構體的名字

}

上面的例子可見,通過Type.String(),Type.Name()方法就可以獲得接口對應的靜態(tài)類型矮烹。 下面幾個方法越庇,顯示了Type的更多功能,特別是對于結構體對象而言奉狈。

上面的例子可見卤唉,通過Type.String(),Type.Name()方法就可以獲得接口對應的靜態(tài)類型。 下面幾個方法仁期,顯示了Type的更多功能桑驱,特別是對于結構體對象而言竭恬。


Type的Field是一個StructFiled對象:

type StructField struct {
    Name    string
    PkgPath string

    Type      Type      // field type
    Tag       StructTag // field tag string
    Offset    uintptr   // offset within struct, in bytes
    Index     []int     // index sequence for Type.FieldByIndex
    Anonymous bool      // is an embedded field
}

Method相關的方法

var f Foo
typ := reflect.TypeOf(f)

fmt.Println(typ.NumMethod()) //1, Foo 方法的個數(shù)
m := typ.Method(0)
fmt.Println(m.Name) //do
fmt.Println(m.Type) //func(main.Foo)
fmt.Println(m.Func) //<func(main.Foo) Value>, 這個返回的是reflect.Value對象熬的,后面再講

Kind

Kind方法Type和Value都有痊硕,它返回的是對象的基本類型,例如int,bool,slice等押框,而不是靜態(tài)類型岔绸。

var f = Foo{}
typ := reflect.TypeOf(f)
fmt.Println(typ)        //main.Foo
fmt.Println(typ.Kind()) //struct

var f2 = &Foo{}
typ2 := reflect.TypeOf(f2)
fmt.Println(typ2)        //*main.Foo
fmt.Println(typ2.Kind()) //ptr

kind()的返回值如下:

const (
  Invalid Kind = iota
  Bool
  Int
  Int8
  Int16
  Int32
  Int64
  Uint
  Uint8
  Uint16
  Uint32
  Uint64
  Uintptr
  Float32
  Float64
  Complex64
  Complex128
  Array
  Chan
  Func
  Interface
  Map
  Ptr
  Slice
  String
  Struct
  UnsafePointer
)

reflect.Value
reflect.ValueOf(i interface{}) Value

reflect.ValueOf()的返回值類型為reflect.Value,它實現(xiàn)了interface{}參數(shù)到reflect.Value的反射

type Foo struct {
  X string
  Y int
}

func (f Foo) do() {
  fmt.Printf("X is: %s, Y is: %d", f.X, f.Y)

}


func main() {
  var i int = 123
  var f = Foo{"abc", 123}
  var s = "abc"
  fmt.Println(reflect.ValueOf(i)) //<int Value>
  fmt.Println(reflect.ValueOf(f)) //<main.Foo Value>
  fmt.Println(reflect.ValueOf(s)) //abc

  //Value.String()方法對string類型的數(shù)據(jù)做了特殊處理,會直接返回字符串的值橡伞。
  //其它類型對象返回的格式都是"<Type% Value>"

}

reflact.Value對象可以通過調用Interface()方法盒揉,再反射回interface{}對象

          reflect.ValueOf()                        Interface()
interface{} ---------------------> reflect.Value -------------------> interface{}

var i int = 123
fmt.Println(reflect.Valueof(i).Interface()) //123

var f = Foo{"abc", 123}
fmt.Println(f) //{abc 123}
fmt.Println(reflect.ValueOf(f).Interface() == f)  //true
fmt.Println(reflect.ValueOf(f).Interface())  //{abc 123}

Value的Field方法

和Type的Filed方法不一樣,Type.Field()返回的是StructFiled對象兑徘,有Name,Type等屬性刚盈,Value.Field()返回的還是一個Value對象。

var foo = Foo{"abc", 123}

val := reflect.ValueOf(foo)
fmt.Println(val.FieldByName("y")) //<int Value>  interface.Value對象

typ := reflect.Typeof(foo)
fmt.Println(typ.FieldByName("y")) //{  <nil>  0 [] false} false StructField對象
func main() {
  var f = Foo{"abc", 123}
  rv := reflect.ValueOf(f)
  rt := reflect.TypeOf(f)
  for i := 0; i < rv.NumField(); i++ {
      fv := rv.Field(i)
      ft := rt.Field(i)
      fmt.Printf("%s type is :%s ,value is %v\n", ft.Name, fv.Type(), fv.Interface())
  }
}

//X type is :string ,value is abc
//Y type is :int ,value is 123

設置Value的值

要設置reflect.Value的值還頗費周折道媚,不能直接對Value進行賦值操作

var s = "abc"
fv := reflect.ValueOf(s)
fmt.Println(fv.CanSet()) //false
// fv.SetString("edf")   //panic

fv2 := reflect.ValueOf(&s)
fmt.Println(fv2.CanSet()) //false
// fv2.SetString("edf")      //panic

relect.Value是字符s的一個反射對象,是不能直接對它進行賦值操作的翘县。 要對s進行賦值最域,需要先拿到s的指針對應的reflect.Value,然后通過Value.Elem()再對應到s,然后才能賦值操作锈麸。 這個地方是相當拗口啊:(

func main() {
  var i int = 123
  fv := reflect.ValueOf(i)
  fe := reflect.ValueOf(&i).Elem()  //必須是指針的Value才能調用Elem
  fmt.Println(fe)       //<int Value>
  fmt.Println(fv)       //<int Value>
  fmt.Println(fv == fe) //false

  fmt.Println(fe.CanSet()) //true
  fe.SetInt(456)
  fmt.Println(i) //456

}

Method

這個是reflect一個比較經(jīng)典的使用場景镀脂,在知道對象方法名的情況下,調用對象的方法忘伞。

type Foo struct {
  X string
  Y int
}

func (f Foo) Do() {
  fmt.Printf("X is: %s, Y is: %d\n", f.X, f.Y)

}

func main() {
  var foo = &Foo{"abc", 123}
  reflect.ValueOf(foo).MethodByName("Do").Call([]reflect.Value{})

}

//方法名Do必須是大寫的薄翅,否則會拋異常

reflect整體不是很好理解,如果要進一步掌握如何使用氓奈,以及在什么場景下用翘魄,建議看一些開源庫的代碼,來理解reflect的使用舀奶。參考:
web.go

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末暑竟,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子育勺,更是在濱河造成了極大的恐慌但荤,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件涧至,死亡現(xiàn)場離奇詭異腹躁,居然都是意外死亡,警方通過查閱死者的電腦和手機南蓬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門纺非,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哑了,“玉大人,你說我怎么就攤上這事铐炫±菔郑” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵倒信,是天一觀的道長科贬。 經(jīng)常有香客問我,道長鳖悠,這世上最難降的妖魔是什么榜掌? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮乘综,結果婚禮上憎账,老公的妹妹穿的比我還像新娘。我一直安慰自己卡辰,他們只是感情好胞皱,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著九妈,像睡著了一般反砌。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上萌朱,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天宴树,我揣著相機與錄音,去河邊找鬼晶疼。 笑死酒贬,一個胖子當著我的面吹牛,可吹牛的內容都是我干的翠霍。 我是一名探鬼主播锭吨,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼寒匙!你這毒婦竟也來了耐齐?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蒋情,失蹤者是張志新(化名)和其女友劉穎埠况,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體棵癣,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡辕翰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了狈谊。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片喜命。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡沟沙,死狀恐怖,靈堂內的尸體忽然破棺而出壁榕,到底是詐尸還是另有隱情矛紫,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布牌里,位于F島的核電站颊咬,受9級特大地震影響,放射性物質發(fā)生泄漏牡辽。R本人自食惡果不足惜喳篇,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望态辛。 院中可真熱鬧麸澜,春花似錦、人聲如沸奏黑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽熟史。三九已至馁害,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間以故,已是汗流浹背蜗细。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工裆操, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留怒详,地道東北人。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓踪区,卻偏偏與公主長得像昆烁,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子缎岗,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內容

  • 首先巴拉巴拉一下golang反射機制的三個定律 1.反射可以從接口類型到反射類型對象 2.反射可以從反射類型對象到...
    吃貓的魚0閱讀 2,915評論 0 1
  • Go語言做Web編程非常方便静尼,并且在開發(fā)效率和程序運行效率方面都非常優(yōu)秀。相比于Java传泊,其最大的優(yōu)勢就是簡便易用...
    暗黑破壞球嘿哈閱讀 9,009評論 6 66
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理鼠渺,服務發(fā)現(xiàn),斷路器眷细,智...
    卡卡羅2017閱讀 134,659評論 18 139
  • 你有一個你喜歡的漫畫家 有一個童話王國的夢 有一本自始至終都很愛的書 有一首聽了多少遍也聽不厭的歌 有一串心愛的手...
    黎小鹿閱讀 213評論 0 0
  • 輪回得久了 忘自己本來是什么 做了什么 想做什么 那些舊事融化成風 刮過我邊緣的葉子 我施以禮節(jié)性的旋轉 舊事舊得...
    我是蝎大人閱讀 309評論 2 3