Go reflect

reflection

  • 反射(reflection)是程序在運(yùn)行時(shí)通過檢查其定義的變量和值獲得對(duì)應(yīng)的真實(shí)類型芜飘。

在計(jì)算機(jī)科學(xué)領(lǐng)域,反射是指一類應(yīng)用能夠自描述和自控制侠姑。此類應(yīng)用采用某種機(jī)制來實(shí)現(xiàn)自己行為的描述(self-representation)和監(jiān)測(cè)(examination)弱恒,并能根據(jù)自身行為的狀態(tài)和結(jié)果酬蹋,調(diào)整或修改應(yīng)用所描述行為的狀態(tài)和相關(guān)的語義萧吠。

  • 反射機(jī)制就是在運(yùn)行時(shí)動(dòng)態(tài)的調(diào)用對(duì)象的方法和屬性

每種編程語言的反射模型都不同左冬,有些語言并不支持反射。支持反射的語言可以在程序編譯期將變量的反射信息纸型,比如字段名稱、類型信息梅忌、結(jié)構(gòu)體信息等整合到可執(zhí)行文件中狰腌,并給程序提供接口訪問反射信息,這樣可在程序運(yùn)行期獲取類型的反射信息牧氮,并有能力改變它們琼腔。

  • 反射是指在程序運(yùn)行期對(duì)程序本身進(jìn)行訪問和修改的能力

程序在編譯時(shí)變量會(huì)被轉(zhuǎn)換為內(nèi)存地址,而變量名不會(huì)被編譯器寫入到可執(zhí)行部分踱葛,因此在運(yùn)行程序時(shí)程序是無法獲取自身信息的丹莲。

reflect

Golang提供了一種機(jī)制在運(yùn)行時(shí)更新和檢查變量的值、調(diào)用變量的方法和變量支持的內(nèi)在操作尸诽,但在編譯時(shí)并不知道這些變量的具體類型甥材,這種機(jī)制稱為反射。

反射可將類型本身作為第一類型的值來處理

  • Golang程序在運(yùn)行期可使用reflect包訪問程序的反射信息

Golang提供了reflect包用來實(shí)現(xiàn)反射性含,通過refelct包能夠完成對(duì)一個(gè)interface{}空接口變量的具體類型和值進(jìn)行獲取洲赵。

Golang程序的反射系統(tǒng)無法獲取到一個(gè)可執(zhí)行文件空間中或一個(gè)包中的所有類型信息,需配合標(biāo)準(zhǔn)庫中對(duì)應(yīng)的此法商蕴、語法解析器和抽象語法樹(AST)對(duì)源碼進(jìn)行掃描后獲取叠萍。

interface

  • Golang關(guān)于類型設(shè)計(jì)的原則

變量包括(value, type)兩部分,type包括static typeconcrete type绪商,簡(jiǎn)單來說static type是在編碼時(shí)可見的類型苛谷,concrete typeruntime系統(tǒng)可見的類型。

類型斷言能夠成功取決于變量的concrete type而非static type格郁,比如一個(gè)變量的concrete type如果實(shí)現(xiàn)了write方法則可被類型斷言為writer腹殿。

  • 反射是建立在類型之上的

Golang指定類型的變量是static的,因此在創(chuàng)建變量時(shí)就已經(jīng)確定理张。反射主要與Golang的interface類型相關(guān)赫蛇,因?yàn)?code>interface的類型是concrete的,只有interface類型才有反射一說雾叭。


  • interfacepair的存在是Golang中實(shí)現(xiàn)反射的前提

在Golang的實(shí)現(xiàn)中每個(gè)interface變量都擁有一對(duì)pair(value, type)悟耘,pair中記錄了實(shí)際變量的值和類型(value, type)
value是實(shí)際變量的值
type是實(shí)際變量的類型

一個(gè)interface{}類型的變量包含兩個(gè)指針:
一個(gè)指針下指向?qū)嶋H的值织狐,即對(duì)應(yīng)的value暂幼。
一個(gè)指針指向值的類型筏勒,對(duì)應(yīng)concrete type

例如:創(chuàng)建類型為*os.File的變量

tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)

將變量賦值給一個(gè)接口變量

var r io.Reader
r = tty

賦值后接口變量的pair中記錄的信息為(tty, *os.File)旺嬉,這個(gè)pair在接口變量連續(xù)賦值過程中是不會(huì)改變的管行。

比如將接口變量賦值給另一個(gè)接口變量

var w io.Writer
w = r.(io.Writer)

此時(shí)接口變量w的pair和r的pair相同,都是(tty, *os.File)邪媳。即使w是空接口類型捐顷,pair也是不變的。

方法

Golang中反射是由reflect包支持的雨效,reflect包提供了兩個(gè)重要的類型TypeValue迅涮。任意接口值在反射中都可以理解為由reflect.Typereflect.Value兩部分組成。

反射是用來檢測(cè)存儲(chǔ)在接口變量?jī)?nèi)部(值 value, 類型 concrete typepair對(duì)的一種機(jī)制徽龟,reflect包提供了reflect.TypeOfreflect.ValueOf兩個(gè)函數(shù)來獲取任意對(duì)象的ValueType叮姑。

  • reflect.ValueOf用于獲取輸入?yún)?shù)接口中的數(shù)據(jù)的值,若接口為空則返回0据悔。
func reflect.ValueOf(i interface{}) reflect.Value
  • reflect.TypeOf用于動(dòng)態(tài)獲取輸入?yún)?shù)接口中值的類型传透,若接口為空則返回nil
func reflect.TypeOf(i interface{}) reflect.Type

reflect.TypeOf()用于獲取pair(value, type)中的type极颓,reflect.ValueOf()獲取獲取pair(value, type)中的value朱盐。

例如:將float64類型的變量作為原值傳遞給reflect.TypeOf(i interface{})獲取其類型信息

var num float64 = 3.14156
t.Log(reflect.ValueOf(num)) //3.14156
t.Log(reflect.TypeOf(num))  //float64

例如:

type Response struct {
    Code    int
    Message string
}

func TestReflect(t *testing.T) {
    var response Response
    t.Log(reflect.ValueOf(response)) //{0 }
    t.Log(reflect.TypeOf(response))  //test.Response
}

定律

反射三大定律

  1. 反射可以將【接口類型變量】轉(zhuǎn)換為【反射類型對(duì)象】
  • 反射類型對(duì)象指的是reflect.Typereflect.Value
  • 反射提供了一種允許程序在運(yùn)行時(shí)檢查接口變量?jī)?nèi)部存儲(chǔ)的pair(value, type)對(duì)的機(jī)制
  • reflect包中的ValueType類型時(shí)訪問接口內(nèi)部的數(shù)據(jù)成為可能
  • reflect包為ValueType類型分別提供了reflect.ValueOf()reflect.TypeOf()方法來進(jìn)行讀取
  1. 反射可以將【反射類型對(duì)象】轉(zhuǎn)換為【接口類型變量】
  • 和物理學(xué)中的反射類似,Golang中的反射也能夠創(chuàng)造自己反面類型的對(duì)象讼昆。
  1. 若要修改【反射類型對(duì)象】則其值必須可寫
  • 可通過value.CanSet()方法來檢查一個(gè)reflect.Value類型的變量是否具有“可寫性”
  • 可寫性類似于尋址能力托享,但更為嚴(yán)格。它是反射類型變量的一種屬性浸赫,會(huì)賦予該變量修改底層存儲(chǔ)數(shù)據(jù)的能力闰围。
  • 可寫性最終是由一個(gè)反射對(duì)象是否存儲(chǔ)原始值而決定的

結(jié)構(gòu)體

  • 使用反射獲取結(jié)構(gòu)體中的字段信息

通過reflect.TypeOf()從結(jié)構(gòu)體中獲取反射類型對(duì)象reflect.Type,可通過reflect.Type獲取結(jié)構(gòu)體成員信息既峡。

結(jié)構(gòu)體成員訪問方法列表羡榴,與成員獲取相關(guān)的reflect.Type的方法。

方法 返回值 描述
type.Field(index int) StructField 根據(jù)索引獲取對(duì)應(yīng)結(jié)構(gòu)體字段
type.NumField() int 獲取結(jié)構(gòu)體成員字段數(shù)量
type.FieldByName(name string) (StructField, bool) 根據(jù)指定字符串獲取對(duì)應(yīng)的結(jié)構(gòu)體字段
type.FieldByIndex(index []int) StructField 多層成員訪問运敢,根據(jù)[]int提供的結(jié)構(gòu)體字段索引獲取字段信息校仑。
type.FieldByNameFunc(match func(string) bool) (StructField, bool) 根據(jù)匹配函數(shù)獲取字段

例如:通過反射值對(duì)象reflect.Value獲取結(jié)構(gòu)體成員信息

package main

import (
    "fmt"
    "reflect"
)

type User struct {
    Id   int
    Name string
}

func main() {
    user := &User{Id: 1, Name: "root"}

    val := reflect.ValueOf(user)
    fmt.Printf("%v\n", val) //&{1 root}

    elem := val.Elem()
    fmt.Printf("%v\n", elem) //{1 root}

    elemType := elem.Type()
    fmt.Printf("%v\n", elemType) //main.User

    numField := elem.NumField()
    fmt.Printf("%v\n", numField) //2

    for i := 0; i < numField; i++ {
        fieldName := elemType.Field(i).Name

        field := elem.Field(i)
        fmt.Printf("%v\n", field)

        fieldType := field.Type()
        fieldValue := field.Interface()

        fmt.Printf("%d: %s %s = %v\n", i, fieldName, fieldType, fieldValue)
    }
}
&{1 root}
{1 root}
main.User
2
1
0: Id int = 1
root
1: Name string = root

reflect.StructField

  • 結(jié)構(gòu)體字段類型

reflect.TypeField()方法返回StructField結(jié)構(gòu),描述了結(jié)構(gòu)體的成員信息传惠,可獲取成員與結(jié)構(gòu)體的關(guān)系迄沫,比如偏移、索引卦方、是否為匿名字段羊瘩、結(jié)構(gòu)體標(biāo)簽等。

type StructField struct{
  Name string //字段名
  PkgPath string //字段路徑
  Type Type //字段反射類型對(duì)象
  Tag StructTag//字段的結(jié)構(gòu)體標(biāo)簽
  Offset uintptr //字段在結(jié)構(gòu)體中的相對(duì)偏移
  Index []int //Type.FieldByIndex返回的索引值
  Anonymous bool//是否為匿名字段
}
字段 類型 描述
Name string 字段名稱
PkgPath string 字段在結(jié)構(gòu)體中的路徑
Type Type 字段本身的反射類型對(duì)象,類型為reflect.Type尘吗。
Tag StructTag 結(jié)構(gòu)體標(biāo)簽逝她,為結(jié)構(gòu)體字段標(biāo)簽的額外信息,可單獨(dú)提取睬捶。
Index FieldByIndex 索引順序
Anonymous bool 字段是否為匿名字段
  • 獲取結(jié)構(gòu)體成員反射信息

例如:實(shí)例化結(jié)構(gòu)體后遍歷成員黔宛,通過reflect.TypeFieldByName()獲取字段信息。

//聲明結(jié)構(gòu)體
type User struct {
    Id   int
    Name string `json:"name" id:"100"`
}
//創(chuàng)建結(jié)構(gòu)體實(shí)例
ins := User{Id: 1, Name: "root"}
//獲取結(jié)構(gòu)體實(shí)例的反射類型對(duì)象
typ := reflect.TypeOf(ins)
//遍歷結(jié)構(gòu)體成員
for i := 0; i < typ.NumField(); i++ {
    //獲取成員
    field := typ.Field(i)
    //獲取成員屬性
    name := field.Name
    tag := field.Tag
    fmt.Printf("name = %v, tag = %v\n", name, tag)
}
//聲明結(jié)構(gòu)體
type User struct {
    Id   int
    Name string `json:"name" id:"100"`
}
//創(chuàng)建結(jié)構(gòu)體實(shí)例
ins := User{Id: 1, Name: "root"}
//獲取結(jié)構(gòu)體實(shí)例的反射類型對(duì)象
typ := reflect.TypeOf(ins)
//通過字段名獲取信息
field, ok := typ.FieldByName("Name")
if ok {
    tag := field.Tag
    tagName := tag.Get("json")
    tagId := tag.Get("id")
    fmt.Printf("name = %v, id = %v\n", tagName, tagId) //name = name, id = 100
}

reflect.StructTag

  • 結(jié)構(gòu)體標(biāo)簽

通過reflect.Type獲取結(jié)構(gòu)體成員reflect.StructField結(jié)構(gòu)中的Tag即結(jié)構(gòu)體標(biāo)簽StructTag擒贸。

結(jié)構(gòu)體標(biāo)簽是對(duì)結(jié)構(gòu)體字段的額外信息標(biāo)簽

JSON臀晃、BSON等格式進(jìn)行序列化,ORM關(guān)系對(duì)象映射都會(huì)使用到結(jié)構(gòu)體標(biāo)簽酗宋,使用標(biāo)簽設(shè)定字段在處理時(shí)應(yīng)具備的特殊屬性和可能發(fā)生的行為积仗。這些信息都是靜態(tài)的,無需實(shí)例化結(jié)構(gòu)體可通過反射獲取到蜕猫。

結(jié)構(gòu)體標(biāo)簽書寫格式:key1:"value1" key2:"value2"

結(jié)構(gòu)體標(biāo)簽由一個(gè)或多個(gè)鍵值對(duì)組成,鍵與值使用冒號(hào)分割哎迄,值使用雙引號(hào)包裹回右,鍵值對(duì)之間使用空格分割。

從結(jié)構(gòu)體標(biāo)簽中獲取值

StructTag擁有方法可以對(duì)Tag信息進(jìn)行解析和提取

  • Get方法根據(jù)Tag中的鍵獲取對(duì)應(yīng)的值
func (tag StructTag) Get(key string) string
  • Lookup方法根據(jù)Tag中的鍵查詢值是否存在
func (tag StructTag) Lookup(key string) (value string, ok bool)

例如:

//聲明結(jié)構(gòu)體
type User struct {
    Id   int    `json:"id" bson:"id"`
    Name string `json:"name" bson:"name" orm:"size(32)"`
}
//創(chuàng)建結(jié)構(gòu)體實(shí)例
ins := User{Id: 1, Name: "root"}
//獲取結(jié)構(gòu)體實(shí)例的反射類型對(duì)象
typ := reflect.TypeOf(ins)
//通過字段名獲取信息
field, ok := typ.FieldByName("Name")
if ok {
    tag := field.Tag

    val, ok := tag.Lookup("bson")
    if ok {
        fmt.Printf("bson = %v\n", val) //bson = name
    }

    val = tag.Get("json")
    fmt.Printf("json = %v\n", val) //json = name
}

反射值對(duì)象

reflect.ValueOf

func reflect.ValueOf(rawValue interface{}) reflect.Value

例如:

val := reflect.ValueOf(nil)
fmt.Printf("%v\n", val) //<invalid reflect.Value>

例如:動(dòng)態(tài)調(diào)用無參數(shù)的結(jié)構(gòu)體方法

package test

import (
    "fmt"
    "reflect"
    "testing"
)

type User struct {
    Id   int
    Name string
}

func (u *User) Do() {
    fmt.Printf("User Do...\n")
}

func TestReflect(t *testing.T) {
    ins := &User{Id: 1, Name: "root"}
    val := reflect.ValueOf(ins)

    fname := "Do"
    val = val.MethodByName(fname)

    val.Call(nil)
}

例如:動(dòng)態(tài)調(diào)用結(jié)構(gòu)體帶參數(shù)的方法

package test

import (
    "fmt"
    "reflect"
    "testing"
)

type User struct {
    Id   int
    Name string
}

func (u *User) Do(id int, msg string) {
    fmt.Printf("%v %v\n", id, msg)
}

func TestReflect(t *testing.T) {
    ins := &User{Id: 1, Name: "root"}
    val := reflect.ValueOf(ins)

    fname := "Do"
    val = val.MethodByName(fname)

    id := reflect.ValueOf(1)
    msg := reflect.ValueOf("hello")
    in := []reflect.Value{id, msg}
    val.Call(in)
}

reflect.Value

reflect.Value
  • reflect.ValueOf(rawValue interface)原值會(huì)轉(zhuǎn)換為類型為reflect.Value的反射值對(duì)象漱挚。
type Value struct {
    typ *rtype
    ptr unsafe.Pointer
    flag
}
  • reflect.Value反射值對(duì)象可與原值通過包裝和值獲取相互轉(zhuǎn)化

通過reflect.Value重新獲取原始值

方法 返回值 描述
Interface() interface{} 通過類型斷言轉(zhuǎn)換為指定類型
Int() int64 將值以int類型返回
Uint() uint64 將值以u(píng)int類型返回
Float() float64 將值以雙精度類型返回
Bool() bool 將值以bool類型返回
Bytes() []byte 將值以字節(jié)數(shù)組類型返回
String() string 將值以字符串類型返回

例如:

var i int = 1024

val := reflect.ValueOf(i)

var j int = val.Interface().(int)
fmt.Printf("j = %v\n", j)

var k int = int(val.Int())
fmt.Printf("k = %v\n", k)
  • 反射值對(duì)象reflect.Value對(duì)零值和空進(jìn)行有效性判斷
方法 返回值 描述
value.IsNil () bool 判斷返回值是否為nil
value.IsValid() bool 判斷返回值是否合法
func (v Value) IsNil() bool

若值類型不是通道翔烁、函數(shù)、接口旨涝、映射蹬屹、指針、切片時(shí)會(huì)發(fā)生panic白华,類似語言層面的v == nil操作慨默。

func (v Value) IsValid() bool

若值本身非法則返回false

例如:判斷空指針

var ptr *int
isNil := reflect.ValueOf(ptr).IsNil()
fmt.Printf("%v\n", isNil) //true

例如:判斷nil是否非法

isValid := reflect.ValueOf(nil).IsValid()
fmt.Printf("%v\n", isValid) //false
  • 通過反射修改變量的值

使用reflect.Value對(duì)包裝的值進(jìn)行修改時(shí),需遵循一定規(guī)則弧腥,若沒有按照規(guī)則進(jìn)行代碼設(shè)計(jì)和編寫厦取,則無法修改對(duì)象的值或運(yùn)行時(shí)發(fā)生宕機(jī)。

反射值對(duì)象的判定及獲取元素的方法

方法 返回值 描述
value.Elem() reflect.Value 獲取值指向的元素值管搪,類似*操作虾攻,當(dāng)值類型不是指針或接口時(shí)會(huì)發(fā)生宕機(jī),空指針返回nilValue更鲁。
value.Addr() reflect.Value 對(duì)可尋址的值返回其地址霎箍,類似&操作,但值不可尋址時(shí)發(fā)生宕機(jī)澡为。
value.CanAddr() bool 值是否可尋址
value.CanSet() bool 值是否可被修改漂坏,要求值可尋址且是可導(dǎo)出的字段。

反射類型對(duì)象

reflect.Type

reflect.Type

根據(jù)傳入的結(jié)構(gòu)體生成SQL語句

func BuildInsertSQL(obj interface{}) string {
    val := reflect.ValueOf(obj)
    typ := reflect.TypeOf(obj)
    //判斷參數(shù)是否為結(jié)構(gòu)體
    if val.Kind() != reflect.Struct {
        panic("unsupported type")
    }
    //獲取結(jié)構(gòu)體名作為數(shù)據(jù)表名
    typename := typ.Name()
    //遍歷結(jié)構(gòu)體字段
    var cols []string
    var vals []string
    for i := 0; i < val.NumField(); i++ {
        //獲取字段名稱
        col := typ.Field(i).Name
        cols = append(cols, fmt.Sprintf("`%s`", col))
        //獲取字段值
        var v string
        field := val.Field(i)
        switch field.Kind() {
        case reflect.String:
            v = field.String()
        case reflect.Int:
            v = strconv.FormatInt(field.Int(), 10)
        default:
        }
        vals = append(vals, v)
    }
    //轉(zhuǎn)換為字符串
    colstr := strings.Join(cols, ",")
    valstr := strings.Join(vals, ",")
    //拼接SQL
    query := fmt.Sprintf("INSERT INTO `%s`(%s) VALUES(%s)", typename, colstr, valstr)
    return query
}
type User struct {
    id   int
    name string
}
user := User{id: 1, name: "admin"}
sql := BuildInsertSQL(user)
fmt.Println(sql)
INSERT INTO `User`(`id`,`name`) VALUES(1,admin)

reflect.Elem

  • 指針與指針指向的元素
func (v reflect.Value) Elem() reflect.Value

Golang對(duì)指針獲取反射對(duì)象時(shí),通過reflect.Elem()方法獲取指針指向的元素類型樊拓,這個(gè)獲取過程被稱為取元素纠亚,等效于對(duì)指針類型變量做了一個(gè)*操作。


Golang反射中對(duì)指針變量的種類都是Ptr

package test

import (
    "fmt"
    "reflect"
    "testing"
)

//聲明空結(jié)構(gòu)體
type User struct {
}

func TestReflect(t *testing.T) {
    //創(chuàng)建指針變量
    ins := &User{}
    //獲取指針變量的反射類型信息
    typ := reflect.TypeOf(ins)

    //獲取指針變量的類型名稱
    name := typ.Name()
    //獲取指針變量的種類
    kind := typ.Kind()
    fmt.Printf("name = %v, kind = %v\n", name, kind) //name = , kind = ptr

    //獲取指針變量的元素類型
    elem := typ.Elem()
    //獲取指針類型元素的名稱
    name = elem.Name()
    //獲取指針類型元素的種類
    kind = elem.Kind()
    fmt.Printf("name = %v, kind = %v\n", name, kind) //name = User, kind = struct
}

value.Elem()方法會(huì)返回反射值對(duì)象所包含的值或指針指向的值筋夏,若反射值對(duì)象的種類Kind不是接口或指針蒂胞,則會(huì)發(fā)生panic。若反射值為0則返回零值条篷。

ptr := (*int)(nil)

val := reflect.ValueOf(ptr)
fmt.Printf("%v\n", val) //<nil>

elem := val.Elem()
fmt.Printf("%v\n", elem) //<invalid reflect.Value>

isValid := elem.IsValid()
fmt.Printf("%v\n", isValid) //false

reflect.Indirect

func reflect.Indirect(v reflect.Value) reflect.Value

Golang的reflect.Indirect()函數(shù)用于獲取v指向的值骗随,若vnil指針則返回零值,若v不為指針返回為v赴叹。

reflect.Elem()方法不同的是鸿染,Elem()方法當(dāng)調(diào)用者的Kind種類不是Interface接口或Ptr指針類型時(shí)衷咽,會(huì)報(bào)panic庆寺。如果調(diào)用者為0,則返回零值津坑。Indirect()的參數(shù)如果為nil指針時(shí)會(huì)返回零值绽媒。

當(dāng)調(diào)用者或參數(shù)reflect.Value為指針時(shí)蚕冬,value.Elem()等效于reflect.Indirect(value)。若不是指針則并非等效是辕。

reflect.Indirect()用于需要接受待定類型或指向該類型的指針囤热。

reflect.Kind

編程中使用最多的是類型(Type),反射中當(dāng)需要區(qū)別一個(gè)大品種的類型時(shí)使用種類(Kind)获三。

例如:需要統(tǒng)一判斷類型中的指針時(shí)旁蔼,使用種類(Kind)較為方便。

Golang中類型(Type)是指系統(tǒng)原生的數(shù)據(jù)類型疙教,比如int棺聊、stringbool松逊、float32等躺屁,以及使用type關(guān)鍵字定義的類型,這些類型的名稱就是其類型本身的名稱经宏。

種類(Kind)指的是對(duì)象歸屬的品種

reflect包中定義的種類Kind

type Kind uint
種類常量 描述
Invalid Kind 非法類型
Bool 布爾型
Int 有符號(hào)整型
Int8 有符號(hào)8位整型
Int16 有符號(hào)16位整型
Int32 有符號(hào)32位整型
Uint 無符號(hào)整型
Uint8 無符號(hào)8位整型
Uint16 無符號(hào)16位整型
Uint32 無符號(hào)32位整型
Uint64 無符號(hào)64位整型
Uintptr 指針
Float32 單精度浮點(diǎn)數(shù)
Float64 雙精度浮點(diǎn)數(shù)
Complex64 64位復(fù)數(shù)類型
Complet128 128位復(fù)數(shù)類型
Array 數(shù)組
Chan 通道
Func 函數(shù)
Interface 接口
Map 映射
Ptr 指針
Slice 切片
String 字符串
Struct 結(jié)構(gòu)體
UnsafePointer 底層指針

Map犀暑、Slice、Chan屬于引用類型烁兰,使用起來類似指針耐亏,但在種類中仍屬于獨(dú)立的種類,而不屬于Ptr沪斟。

例如:

type User struct{}

User結(jié)構(gòu)體屬于Struct種類广辰,*User屬于Ptr種類暇矫。


從類型對(duì)象reflect.Type中獲取類型名稱Name和種類Kind

  • 類型名稱對(duì)應(yīng)的反射獲取方法是reflect.Type中的Name()方法,返回表示類型名稱的字符串择吊。
  • 類型歸屬的種類Kind使用的是reflect.Type中的Kind()方法李根,返回reflect.Kind類型的常量。

例如:從常量中獲取類型信息

package test

import (
    "fmt"
    "reflect"
    "testing"
)

//定義枚舉類型
type Enum int

//定義常量
const (
    Zero Enum = 0
)

func TestReflect(t *testing.T) {
    typ := reflect.TypeOf(Zero)
    name := typ.Name()                               //Enum
    kind := typ.Kind()                               //int
    fmt.Printf("Name = %v, Kind = %v\n", name, kind) //Name = Enum, Kind = int
}

例如:從結(jié)構(gòu)體中獲取類型信息

package test

import (
    "fmt"
    "reflect"
    "testing"
)

type Response struct {
    Code    int
    Message string
}

func TestReflect(t *testing.T) {
    //結(jié)構(gòu)體實(shí)例
    response := Response{}
    //獲取結(jié)構(gòu)體實(shí)例的反射類型對(duì)象
    typ := reflect.TypeOf(response)
    //獲取反射類型對(duì)象的名稱和種類
    name := typ.Name()                               //Response
    kind := typ.Kind()                               //struct
    fmt.Printf("Name = %v, Kind = %v\n", name, kind) //Name = Response, Kind = struct
}

type.Implements

  • 判斷實(shí)例是否已實(shí)現(xiàn)接口

創(chuàng)建接口

type IT interface {
    test()
}

創(chuàng)建結(jié)構(gòu)體并實(shí)現(xiàn)接口方法

type T struct {
}

func (t *T) test() {
    fmt.Printf("T struct test run...\n")
}

判斷結(jié)構(gòu)體是否實(shí)現(xiàn)接口

t1 := reflect.TypeOf(&T{})
t2 := reflect.TypeOf((*IT)(nil)).Elem()

ok := t1.Implements(t2)
fmt.Printf("%v\n", ok)

reflect.Addr

func (v reflect.Value) reflect.Addr() reflect.Value

Golang中的reflect.Addr()函數(shù)用于獲取表示v地址的指針值

reflect.New

func reflect.New(typ reflect.Type) reflect.Value

reflect.SliceOf

func reflect.SliceOf(t reflect.Type) reflect.Type
  • reflect.SliceOf() 函數(shù)用于獲取元素類型為t的切片類型几睛,如果t表示int房轿,SliceOf(t)表示[]int

value.Call

  • 反射中函數(shù)和方法的調(diào)用
func (v reflect.Value) Call(in []reflect.Value) []reflect.Value
  • 反射中調(diào)用函數(shù)

例如:Golang中函數(shù)類型可以像普通類型等類型變量作為值來使用

func test(){
    fmt.Println("fn running....")
}
func main(){
    fn := test
    fn()//fn running....
}

既然函數(shù)可以像普通類型變量一樣所森,那么在反射機(jī)制中和不同的變量是一樣的囱持。

反射中函數(shù)和方法的類型(Type)都是reflect.Func,若需調(diào)用函數(shù)可通過ValueCall()方法實(shí)現(xiàn)焕济。

func test(){
    fmt.Println("fn running....")
}
func main(){
    fn := test
    rv := reflect.ValueOf(fn)
    if rv.Kind() != reflect.Func{
        fmt.Println("INVIDAD TYPE")
    }
    rv.Call(nil)
}

reflect.ValueCall()方法的參數(shù)是一個(gè)[] reflect.Value切片纷妆,對(duì)應(yīng)的反射函數(shù)類型的類型,返回值也是一個(gè)reflect.Value[] reflect.Value切片晴弃,同樣對(duì)應(yīng)反射函數(shù)類型的返回值掩幢。

func test(i int) string{
    fmt.Printf("fn running.... %d\n", i)
    return strconv.Itoa(i)
}
func main(){
    fn := test
    //判斷類型
    rv := reflect.ValueOf(fn)
    if rv.Kind() != reflect.Func{
        fmt.Println("INVALID TYPE")
    }
    //設(shè)置參數(shù)
    args := make([]reflect.Value, 1)
    args[0] = reflect.ValueOf(100)
    //調(diào)用函數(shù)
    vs := rv.Call(args)
    fmt.Printf("%v\n", vs)//[100]
    fmt.Printf("%v\n", vs[0])//100
    rs := vs[0].Interface().(string)
    fmt.Printf("%v\n", rs)//100
}
  • 反射中調(diào)用方法

反射中方法的調(diào)用,函數(shù)和方法本質(zhì)上是相同的上鞠,只不過方法與一個(gè)對(duì)象進(jìn)行了綁定粒蜈,方法是對(duì)象的一種行為,這種行為是對(duì)于這個(gè)對(duì)象的一系列操作旗国。

type Users struct{
    id int
    name string
}
func (u *Users) SetId(id int){
    u.id = id
}
func (u *Users) SetName(name string){
    u.name = name
}
func (u *Users) String() string{
    return fmt.Sprintf("%p", u) + " name:"+u.name + " id:" + strconv.Itoa(u.id)
}
func main(){
    u := &Users{1, "root"}

    rv := reflect.ValueOf(&u).Elem()

    vs := rv.MethodByName("String").Call(nil)
    fmt.Printf("%v\n", vs[0])//0xc000004480 name:root id:1

    args := make([]reflect.Value, 1)
    args[0] = reflect.ValueOf(100)
    vs = rv.MethodByName("SetId").Call(args)
    fmt.Printf("%v\n", vs)//[]

    args[0] = reflect.ValueOf("admin")
    vs = rv.MethodByName("SetName").Call(args)
    fmt.Printf("%v\n", vs)//[]

    vs = rv.MethodByName("String").Call(nil)
    fmt.Printf("%v\n", vs[0])//0xc000004480 name:admin id:100
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市注整,隨后出現(xiàn)的幾起案子能曾,更是在濱河造成了極大的恐慌,老刑警劉巖肿轨,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件寿冕,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡椒袍,警方通過查閱死者的電腦和手機(jī)驼唱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來驹暑,“玉大人玫恳,你說我怎么就攤上這事∮欧” “怎么了京办?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)帆焕。 經(jīng)常有香客問我惭婿,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任财饥,我火速辦了婚禮换吧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘钥星。我一直安慰自己沾瓦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布打颤。 她就那樣靜靜地躺著暴拄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪编饺。 梳的紋絲不亂的頭發(fā)上乖篷,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音透且,去河邊找鬼撕蔼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛秽誊,可吹牛的內(nèi)容都是我干的鲸沮。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼锅论,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼讼溺!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起最易,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤怒坯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后藻懒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體剔猿,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年嬉荆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了归敬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鄙早,死狀恐怖汪茧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蝶锋,我是刑警寧澤陆爽,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站扳缕,受9級(jí)特大地震影響慌闭,放射性物質(zhì)發(fā)生泄漏别威。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一驴剔、第九天 我趴在偏房一處隱蔽的房頂上張望省古。 院中可真熱鬧,春花似錦丧失、人聲如沸豺妓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽琳拭。三九已至,卻和暖如春描验,著一層夾襖步出監(jiān)牢的瞬間白嘁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工膘流, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留絮缅,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓呼股,卻偏偏與公主長(zhǎng)得像耕魄,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子彭谁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Go reflect 反射實(shí)例解析 ——教壞小朋友系列 0 FBI WARNING 對(duì)于本文內(nèi)容吸奴,看懂即可,完全不...
    楊浥塵閱讀 699評(píng)論 0 3
  • 轉(zhuǎn)載自: Go Reflect 最近在看一些go語言標(biāo)準(zhǔn)庫以及第三方庫的源碼時(shí)缠局,發(fā)現(xiàn)go的reflect被大量使用...
    lucasdada閱讀 382評(píng)論 0 1
  • 在 reflect 包中奄抽,主要通過兩個(gè)函數(shù) TypeOf() 和 ValueOf() 實(shí)現(xiàn)反射,TypeOf() ...
    隨風(fēng)化作雨閱讀 504評(píng)論 0 1
  • 一甩鳄、認(rèn)識(shí)反射 維基百科中的定義:在計(jì)算機(jī)科學(xué)中,反射是指計(jì)算機(jī)程序在運(yùn)行時(shí)(Run time)可以訪問额划、檢測(cè)和修改...
    Every_dawn閱讀 1,588評(píng)論 0 0
  • Reflect 本文側(cè)重講解reflect反射的實(shí)踐應(yīng)用妙啃,適合新手初窺門徑。 reflect兩個(gè)基本功能 refl...
    tinsonHo閱讀 382評(píng)論 0 0