1. 程式人生 > >Golang Reflect反射

Golang Reflect反射

Go是靜態型別語言。每個變數都擁有一個靜態型別,這意味著每個變數的型別在編譯時都是確定的:int,float32, *AutoType, []byte,  chan []int 諸如此類。

動靜型別

     編譯時就知道變數型別的是靜態型別;執行時才知道一個變數型別的叫做動態型別。

    1. 靜態型別

            靜態型別就是變數宣告時的賦予的型別。比如:

type MyInt int // int 就是靜態型別

type A struct{
    Name string  // string就是靜態
}
var i *int  // *int就是靜態型別

    2. 動態型別

    動態型別:執行時給這個變數複製時,這個值的型別(如果值為nil的時候沒有動態型別)。一個變數的動態型別在執行時可能改變,這主要依賴於它的賦值(前提是這個變數時介面型別)。

    var A interface{} // 靜態型別interface{}
	A = 10            // 靜態型別為interface{}  動態為int
	A = "String"      // 靜態型別為interface{}  動態為string
	var M *int
	A = M             // 猜猜這個呢?

    來看看這個例子:

//定義一個介面
type Abc interface
{ String() string } // 型別 type Efg struct{ data string } // 型別Efg實現Abc介面 func (e *Efg)String()string{ return e.data } // 獲取一個*Efg例項 func GetEfg() *Efg{ return nil } // 比較 func CheckAE(a Abc) bool{ return a == nil } func main() { efg := GetEfg() b := CheckAE(efg) fmt.Println(b) os.Exit(1)

關於動靜態型別就到這裡,詳細請自行Google,百度吧。

反射

      那麼什麼時候下使用反射呢?

      有時候你想在執行時使用變數來處理變數,這些變數使用編寫程式時不存在的資訊。也許你正試圖將來自檔案或網路請求的資料對映到變數中。也許建立一個適用於不同型別的tool。在這些情況下,你需要使用反射。反射使您能夠在執行時檢查型別。它還允許您在執行時檢查,修改和建立變數,函式和結構。

    型別

      你可以使用反射來獲取變數的型別: var t := reflect.Typeof(v)。返回值是一個reflect.Type型別。該值有很多定義好的方法可以使用。

            Name()

      返回型別的名稱。 但是像切片或指標是沒有型別名稱的,只能返回空字串。

            Kind()

      Kind有slice, map , pointer指標,struct, interface, string , Array, Function, int或其他基本型別組成。Kind和Type之前要做好區分。如果你定義一個 type Foo struct {}, 那麼Kind就是struct,  Type就是Foo。

        *小知識點:反射變數對應的Kind方法的返回值是基型別,並不是靜態型別。下面的例子中:

type MyInt int
var x MyInt = 7
v := reflect.ValueOf(x)

變數v的Kind依舊是reflect.Int,而不是MyInt這個靜態型別。Type可以表示靜態型別,而Kind不可以。

       *注意點: 在使用refelct包時, reflect包會假定你已經知道所做的是什麼,否則引發panic。 例如你呼叫了與當前reflect.Type 不同的型別上的方法,那麼就會引發panic。

            Elem()

        如果你的變數是一個指標、map、slice、channel、Array。那麼你可以使用reflect.Typeof(v).Elem()來確定包含的型別。

           案例程式碼    

type Foo struct {
	A int `tag1:"Tag1" tag2:"Second Tag"`
	B string
}
func main(){
	// Struct
	f := Foo{A: 10, B: "Salutations"}
	// Struct型別的指標
	fPtr := &f
	// Map
	m := map[string]int{"A": 1 , "B":2}
	// channel
	ch := make(chan int)
	// slice
	sl:= []int{1,32,34}
	//string
	str := "string var"
	// string 指標
	strPtr := &str

	tMap := examiner(reflect.TypeOf(f), 0)
	tMapPtr := examiner(reflect.TypeOf(fPtr), 0)
	tMapM := examiner(reflect.TypeOf(m), 0)
	tMapCh := examiner(reflect.TypeOf(ch), 0)
	tMapSl := examiner(reflect.TypeOf(sl), 0)
	tMapStr := examiner(reflect.TypeOf(str), 0)
	tMapStrPtr := examiner(reflect.TypeOf(strPtr), 0)

	fmt.Println("tMap :", tMap)
	fmt.Println("tMapPtr: ",tMapPtr)
	fmt.Println("tMapM: ",tMapM)
	fmt.Println("tMapCh: ",tMapCh)
	fmt.Println("tMapSl: ",tMapSl)
	fmt.Println("tMapStr: ",tMapStr)
	fmt.Println("tMapStrPtr: ",tMapStrPtr)
}

// 型別以及元素的型別判斷
func examiner(t reflect.Type, depth int) map[int]map[string]string{
	outType := make(map[int]map[string]string)

	// 如果是一下型別,重新驗證
	switch t.Kind() {
	case reflect.Array, reflect.Chan, reflect.Map, reflect.Ptr, reflect.Slice:
		fmt.Println("這幾種型別Name是空字串:",t.Name(), ", Kind是:", t.Kind())
		// 遞迴查詢元素型別
		tMap := examiner(t.Elem(), depth)
		for k, v := range tMap{
			outType[k] = v
		}

	case reflect.Struct:
		for i := 0; i < t.NumField(); i++ {
			f := t.Field(i) // reflect欄位
			outType[i] = map[string]string{
				"Name":f.Name,
				"Kind":f.Type.String(),
			}
		}
	default:
		// 直接驗證型別
		outType = map[int] map[string]string{depth:{"Name":t.Name(), "Kind":t.Kind().String()}}
	}

	return outType
}

執行結果:

其中t.Field(index) 必須使用在Struct上 ,  所以,細讀文件才行

 利用反射讀取,設定,建立

        看完了上面關於reflect檢測變數型別外,我們使用反射讀取、設定和建立。

    要想讀取一個變數的值,首先需要一個reflect.Valueof( var ) 例項(reflectVal := reflect.Valueof(var)), 同時也可以獲取變數的型別了。

       要想修改一個變數的值,那麼必須通過該變數的指標地址 , 取消指標的引用  。通過refPtrVal := reflect.Valueof( &var )的方式獲取指標型別,你使用refPtrVal.elem( ).set(一個新的reflect.Value)來進行更改,傳遞給set()的值也必須是一個reflect.value。

        要想建立一個值,那麼使用NewPtrVal := reflect.New( vartype ) 傳遞一個reflect.Type型別。 返回的指標型別就可以使用以上修改的方式寫入值。

        最後,你可以通過呼叫interface()方法返回一個正常的變數。因為Golang沒有泛型,變數的原始型別丟失;該方法返回一個型別為interface{} 的值。如果建立了一個指標以便可以修改該值,則需要使用elem().interface()來反引用reflect的指標。在這兩種情況下,您都需要將空介面轉換為實際型別才能使用它。

例項程式碼:

type Foo struct {
	A int `tag1:"Tag1" tag2:"Second Tag"`
	B string
}
func main(){
	// 反射的使用
	s := "String字串"
	fo := Foo{A: 10, B: "欄位String字串"}

	sVal := reflect.ValueOf(s)
	// 在沒有獲取指標的前提下,我們只能讀取變數的值。
	fmt.Println(sVal.Interface())

	sPtr := reflect.ValueOf(&s)
	sPtr.Elem().Set(reflect.ValueOf("修改值1"))
	sPtr.Elem().SetString("修改值2")
	// 修改指標指向的值,原變數改變
	fmt.Println(s)
	fmt.Println(sPtr) // 要注意這是一個指標變數,其值是一個指標地址

	foType := reflect.TypeOf(fo)
	foVal := reflect.New(foType)
	// foVal.Elem().Field(0).SetString("A") // 引發panic
	foVal.Elem().Field(0).SetInt(1)
	foVal.Elem().Field(1).SetString("B")
	f2 := foVal.Elem().Interface().(Foo)
	fmt.Printf("%+v, %d, %s\n", f2, f2.A, f2.B)

執行結果:

    

記憶10秒。

建立slice, map, chan

除了建立內建和使用者定義型別的例項之外,還可以使用反射來建立通常需要make功能的例項。使用reflect.Makeslice,reflect.Makemap和reflect.Makechan函式來製作slice,map或channel。

    // 反射建立map slice channel
	intSlice := make([]int, 0)
	mapStringInt := make(map[string]int)
	sliceType := reflect.TypeOf(intSlice)
	mapType := reflect.TypeOf(mapStringInt)

	// 建立新值
	intSliceReflect := reflect.MakeSlice(sliceType, 0, 0)
	mapReflect := reflect.MakeMap(mapType)

	// 使用新建立的變數
	v := 10
	rv := reflect.ValueOf(v)
	intSliceReflect = reflect.Append(intSliceReflect, rv)
	intSlice2 := intSliceReflect.Interface().([]int)
	fmt.Println(intSlice2)

	k := "hello"
	rk := reflect.ValueOf(k)
	mapReflect.SetMapIndex(rk, rv)
	mapStringInt2 := mapReflect.Interface().(map[string]int)
	fmt.Println(mapStringInt2)

執行結果:

            

建立函式

使用reflect.Makefunc()建立,這個函式需要我們想要做的函式的reflect.type和一個輸入引數是[] reflect.value型別的slice,其輸出引數也是型別[] reflect.value的閉包。下面是一個簡單的例子,檢測任意給定函式的執行時長:

package main

import (
	"reflect"
	"time"
	"fmt"
	"runtime"
)
/*
將建立Func封裝, 非reflect.Func型別會panic
當然makeFunc的閉包函式表示式型別是固定的,可以查閱一下文件。
細讀文件的reflect.Value.Call()方法。
 */
func MakeTimedFunction(f interface{}) interface{} {
	rf := reflect.TypeOf(f)
	if rf.Kind() != reflect.Func {
		panic("非Reflect.Func")
	}
	vf := reflect.ValueOf(f)
	wrapperF := reflect.MakeFunc(rf, func(in []reflect.Value) []reflect.Value {
		start := time.Now()
		out := vf.Call(in)
		end := time.Now()
		fmt.Printf("calling %s took %v\n", runtime.FuncForPC(vf.Pointer()).Name(), end.Sub(start))
		return out
	})
	return wrapperF.Interface()
}

func time1() {
	fmt.Println("time1Func===starting")
	time.Sleep(1 * time.Second)
	fmt.Println("time1Func===ending")
}

func time2(a int) int {
	fmt.Println("time2Func===starting")
	time.Sleep(time.Duration(a) * time.Second)
	result := a * 2
	fmt.Println("time2Func===ending")
	return result
}

func main(
            
           

相關推薦

Golang Reflect反射

Go是靜態型別語言。每個變數都擁有一個靜態型別,這意味著每個變數的型別在編譯時都是確定的:int,float32, *AutoType, []byte,  chan []int 諸如此類。 動靜型別      編譯時就知道變數型別的是靜態型別;執行時才知道一個變數型

golang-利用反射給結構體賦值

cnblogs logs val valueof blog eof 成員 str byname 由於想給一個結構體的部分成員賦值,但是有不知道具體名字,故將tag的json名字作為索引,按照json名字來一一賦值 //將結構體裏的成員按照json名字來賦值 func

GO語言使用之Reflect(反射)

一、從案列場景引入反射 定義了兩個函式test1和test2,定義一個介面卡函式用作統一處理介面: (1) 定義了兩個函式 test1 := func(v1 int, v2 int) { t.Log(v1, v2) } test2 := func(v1 int, v2

Java- Reflect 反射 學習 總結

目錄 Class 類的使用 動態載入類 獲取方法資訊 獲取成員資訊 獲取構造方法 方法的反射 泛型的本質 學習總結 1、Class 類的使用         

Java原始碼分析——java.lang.reflect反射包解析(三) 動態代理、Proxy類、WeakCache類

    代理模式是一個經常被各種框架使用的模式,比如Spring AOP、Mybatis中就經常用到,當一個類訪問另外一個類困難時,可通過一個代理類來間接訪問,在Java中,為了保證程式的簡單性,代理類與目標類需要實現相同的介面。也就是說代理模式起

Java原始碼分析——java.lang.reflect反射包解析(二) Array類,陣列的建立

    在Java中,引用型別中有那麼幾個特殊的類,Object類是所有類的起源、Class類定義所有類的抽象與行為、ClassLoader類實現了類從.class檔案中載入進jvm,而Array陣列類,則實現了陣列手動的建立。  &

Java原始碼分析——java.lang.reflect反射包解析(一) AccessibleObject、ReflectionFactory、Filed、Method、Constructor類

    Java的反射機制一直是被人稱讚的,它的定義是:程式在執行中時,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意一個方法和屬性。簡單的來說就是可以通過Java的反射機制知道自己想知道的類的一切資訊。

golang reflect

在計算機中,反射表示程式能夠檢查自身結構的一種能力,尤其是型別。通過反射,可以獲取物件型別的詳細資訊,並可動態操作物件。 實現 包手冊地址:https://studygolang.com/static/pkgdoc/pkg/reflect.htm Import ("reflect") 常用的主

[golang]golang reflect詳細用法整理

本部落格原創博文版權所有 @[email protected] 僅供交流學習使用用於商業用途請聯絡原作者  轉載請註明出處:http://blog.sina.com.cn/ally2014

java Introspector內省和Reflect反射學習、聯絡和區別

                        java Introspector內省和Reflect反射學習、聯絡和區別   一、反射和內省 1、反射: 將

java reflect反射獲取方法變數引數

類的成員包含變數(Field),方法(Method),構造器(Constructor) 類定義 package Reflect; public class MyTest { public int a; public static int b; public static f

golang通過反射動態呼叫方法

package main import ( "fmt" "reflect" "errors" ) func Call(m map[string]interface{}, name string, params ...interface{}) ([]reflect.Value, er

golang利用反射設定結構體變數的值

如果需要動態設定struct變數field的情況下, 可以利用reflect來完成. 程式碼如下: package main import ( "fmt" "reflect" ) // 定義結構

SpringMVC原始碼學習之request處理流程 springMVC原始碼學習地址 springMVC原始碼學習之addFlashAttribute原始碼分析 java reflect反射呼叫方法invoke

目的:為看原始碼提供呼叫地圖,最長呼叫邏輯深度為8層,反正我是springMVC原始碼學習地址看了兩週才理出來的。 1.處理流程(版本為4.3.18) 入口為spring-webmvc-4.3.18.RELEASE.jar中org.springframework.web.servlet.Dispatche

java-reflect反射

概述 JAVA反射機制是在執行狀態中;對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意一個方法和屬性;這種動態獲取的資訊以及動態呼叫物件的方法的功能稱為java語言的反射機制。 獲取Class的三種方法 /** * 獲取Clas

Java reflect 反射學習筆記

clas parameter 告訴 關鍵字 對象 報錯 getclass exc == class 類的使用 萬事萬物皆對象 (基本數據類型, 靜態成員不是面向對象), 所以我們創建的每一個類都是對象, 即類本身是java.lang.Class類的實例對象, 但是這些

[Golang語言社群]--提高 golang反射效能

golang 的反射很慢。這個和它的 api 設計有關。在 java 裡面,我們一般使用反射都是這樣來弄的。 Field field = clazz.getField("hello"); field.get(obj1); field.get(obj2); 這個取得的

JAVA 構造器,extends[繼承],implements[實現],Interface[介面],reflect[反射],clone[克隆],final,static,abstract[抽象]

構造器[建構函式]: 在java中如果使用者編寫類的時候沒有提供建構函式,那麼編譯器會自動提供一個預設建構函式.它會把所有的例項欄位設定為預設值:所有的數字變數初始化為0;所有的布林變數設定為false;所有物件變數設定為null; PS: 只有在類中沒有其它構造器的時

Golang反射機制(The Laws of Reflection)

Introduction(簡介) 反射機制能夠在陳故鄉執行過程中檢查自身元素的結構,型別;屬於元程式程式設計。但同時也帶來了不少迷惑。 本文我們嘗試通過解釋Go中的反射機制來解釋一些使用細節。每種語言的反射機制都是不同的(有很多語言甚至沒有反射),此文針

Go reflect反射

UNC i++ php get import 返回 field rdquo 通過 reflect包實現了運行時反射,允許程序操作任意類型的對象。典型用法是用靜態類型interface{}保存一個值,通過調用TypeOf獲取其動態類型信息,該函數返回一個Type類型值