日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

详解Golang的反射(实例)

發(fā)布時間:2023/12/29 综合教程 42 生活家
生活随笔 收集整理的這篇文章主要介紹了 详解Golang的反射(实例) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

下面由golang教程欄目給大家介紹詳解Golang的反射(實例),希望對需要的朋友有所幫助!

前言

反射在很多語言中都有其妙用。在計算機科學(xué)領(lǐng)域,反射是指一類應(yīng)用,它們能夠***自描述***和***自控制***。

本文將記錄筆者對于Golang的反射的筆記。

10s后,以下知識點即將靠近:
1.反射的簡介
2.為什么使用反射?
3.反射具體能做什么

正文1.反射的簡介

Golang提供了一種機制,在編譯時不知道類型的情況下,可更新變量、運行時查看值、調(diào)用方法以及直接對他們的布局進行操作的機制,稱為反射。

2.為什么使用反射?

打個比方,有時候我們需要一個函數(shù)可以處理各種類型的值。在不知道類型的情況下,你可能會這么寫:

// 偽代碼
switch value := value.(type) {
case string:
	// ...一些操作
case int:	
	// ...一些操作	
case cbsStruct: // 自定義的結(jié)構(gòu)體	
	// ...一些操作

// ...
}

有沒發(fā)現(xiàn)什么問題?
這邊存在一個問題:類型很多,這個函數(shù)會寫的非常長,而且還可能存在自定的類型,也就是說這個判斷日后可能還要一直改,因為無法知道未知值到底屬于什么類型。

無法透視一個未知類型的時候,以上代碼其實不是很合理,這時候就需要有反射來幫忙你處理,反射使用TypeOf和ValueOf函數(shù)從接口中獲取目標(biāo)對象的信息,輕松完成目的。

3.反射具體能做什么?

1.獲取變量內(nèi)部信息

reflect提供了兩種類型來進行訪問接口變量的內(nèi)容:

類型 作用

reflect.ValueOf()獲取輸入?yún)?shù)接口中的數(shù)據(jù)的值,如果為空則返回0 <- 注意是0reflect.TypeOf()動態(tài)獲取輸入?yún)?shù)接口中的值的類型,如果為空則返回nil <- 注意是nil

上代碼

package main

import (
	"fmt"
	"reflect"
)

func main() {
	var name string = "咖啡色的羊駝"

	// TypeOf會返回目標(biāo)數(shù)據(jù)的類型,比如int/float/struct/指針等
	reflectType := reflect.TypeOf(name)

	// valueOf返回目標(biāo)數(shù)據(jù)的的值,比如上文的"咖啡色的羊駝"
	reflectValue := reflect.ValueOf(name)

	fmt.Println("type: ", reflectType)
	fmt.Println("value: ", reflectValue)
}

輸出:

type:  string
value:  咖啡色的羊駝

更深一層:在以上操作發(fā)生的時候,反射將“接口類型的變量”轉(zhuǎn)為了“反射的接口類型的變量”,比如上文實際上返回的是reflect.Value和reflect.Type的接口對象。(可以根據(jù)ide跟蹤一下相關(guān)函數(shù)返回類型便知)

2.struct的反射

package main

import (
	"fmt"
	"reflect"
)

type Student struct {
	Id   int
	Name string
}

func (s Student) Hello(){
	fmt.Println("我是一個學(xué)生")
}

func main() {
	s := Student{Id: 1, Name: "咖啡色的羊駝"}

	// 獲取目標(biāo)對象
	t := reflect.TypeOf(s)
	// .Name()可以獲取去這個類型的名稱
	fmt.Println("這個類型的名稱是:", t.Name())

	// 獲取目標(biāo)對象的值類型
	v := reflect.ValueOf(s)
	// .NumField()來獲取其包含的字段的總數(shù)
	for i := 0; i < t.NumField(); i++ {
		// 從0開始獲取Student所包含的key
		key := t.Field(i)

		// 通過interface方法來獲取key所對應(yīng)的值
		value := v.Field(i).Interface()

		fmt.Printf("第%d個字段是:%s:%v = %v \\n", i+1, key.Name, key.Type, value)
	}

	// 通過.NumMethod()來獲取Student里頭的方法
	for i:=0;i<t.NumMethod(); i++ {
		m := t.Method(i)
		fmt.Printf("第%d個方法是:%s:%v\\n", i+1, m.Name, m.Type)
	}
}

輸出:

這個類型的名稱是: Student
第1個字段是:Id:int = 1 
第2個字段是:Name:string = 咖啡色的羊駝 
第1個方法是:Hello:func(main.Student)

3.匿名或嵌入字段的反射

package main

import (
	"reflect"
	"fmt"
)

type Student struct {
	Id   int
	Name string
}

type People struct {
	Student // 匿名字段
}

func main() {
	p := People{Student{Id: 1, Name: "咖啡色的羊駝"}}

	t := reflect.TypeOf(p)
	// 這里需要加一個#號,可以把struct的詳情都給打印出來
	// 會發(fā)現(xiàn)有Anonymous:true,說明是匿名字段
	fmt.Printf("%#v\\n", t.Field(0))

	// 取出這個學(xué)生的名字的詳情打印出來
	fmt.Printf("%#v\\n", t.FieldByIndex([]int{0, 1}))

	// 獲取匿名字段的值的詳情
	v := reflect.ValueOf(p)
	fmt.Printf("%#v\\n", v.Field(0))
}

輸出:

reflect.StructField{Name:"Student", PkgPath:"", Type:(*reflect.rtype)(0x10aade0), Tag:"", Offset:0x0, Index:[]int{0}, Anonymous:true}

reflect.StructField{Name:"Name", PkgPath:"", Type:(*reflect.rtype)(0x109f4e0), Tag:"", Offset:0x8, Index:[]int{1}, Anonymous:false}

main.Student{Id:1, Name:"咖啡色的羊駝"}

4.判斷傳入的類型是否是我們想要的類型

package main

import (
	"reflect"
	"fmt"
)

type Student struct {
	Id   int
	Name string
}

func main() {
	s := Student{Id: 1, Name: "咖啡色的羊駝"}
	t := reflect.TypeOf(s)

	// 通過.Kind()來判斷對比的值是否是struct類型
	if k := t.Kind(); k == reflect.Struct {
		fmt.Println("bingo")
	}

	num := 1;
	numType := reflect.TypeOf(num)
	if k := numType.Kind(); k == reflect.Int {
		fmt.Println("bingo")
	}
}

輸出:

bingo
bingo

5.通過反射修改內(nèi)容

package main

import (
	"reflect"
	"fmt"
)

type Student struct {
	Id   int
	Name string
}

func main() {
	s := &Student{Id: 1, Name: "咖啡色的羊駝"}

	v := reflect.ValueOf(s)

	// 修改值必須是指針類型否則不可行
	if v.Kind() != reflect.Ptr {
		fmt.Println("不是指針類型,沒法進行修改操作")
		return
	}

	// 獲取指針?biāo)赶虻脑?	v = v.Elem()

	// 獲取目標(biāo)key的Value的封裝
	name := v.FieldByName("Name")

	if name.Kind() == reflect.String {
		name.SetString("小學(xué)生")
	}

	fmt.Printf("%#v \\n", *s)


	// 如果是整型的話
	test := 888
	testV := reflect.ValueOf(&test)
	testV.Elem().SetInt(666)
	fmt.Println(test)
}

輸出:

main.Student{Id:1, Name:"小學(xué)生"} 
666

6.通過反射調(diào)用方法

package main

import (
	"fmt"
	"reflect"
)

type Student struct {
	Id   int
	Name string
}

func (s Student) EchoName(name string){
	fmt.Println("我的名字是:", name)
}

func main() {
	s := Student{Id: 1, Name: "咖啡色的羊駝"}

	v := reflect.ValueOf(s)

	// 獲取方法控制權(quán)
	// 官方解釋:返回v的名為name的方法的已綁定(到v的持有值的)狀態(tài)的函數(shù)形式的Value封裝
	mv := v.MethodByName("EchoName")
	// 拼湊參數(shù)
	args := []reflect.Value{reflect.ValueOf("咖啡色的羊駝")}

	// 調(diào)用函數(shù)
	mv.Call(args)
}

輸出:

我的名字是: 咖啡色的羊駝

##4.反射的一些小點

1.使用反射時需要先確定要操作的值是否是期望的類型,是否是可以進行“賦值”操作的,否則reflect包將會毫不留情的產(chǎn)生一個panic。

2.反射主要與Golang的interface類型相關(guān),只有interface類型才有反射一說。如果有興趣可以看一下TypeOf和ValueOf,會發(fā)現(xiàn)其實傳入?yún)?shù)的時候已經(jīng)被轉(zhuǎn)為接口類型了。

// 以下為截取的源代碼
func TypeOf(i interface{}) Type {
	eface := *(*emptyInterface)(unsafe.Pointer(&i))
	return toType(eface.typ)
}

func ValueOf(i interface{}) Value {
	if i == nil {
		return Value{}
	}
	escapes(i)

	return unpackEface(i)
}

總結(jié)

以上是生活随笔為你收集整理的详解Golang的反射(实例)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。