Reflect:Go 語言的鏡子

閱讀時間約 16 分鐘


raw-image

👨‍💻簡介

在 Go 語言中,reflect package是用來檢查和操作變數的type、value和struct。常見用法有檢察 type、調用方法,以及修改變數的value。今天簡單介紹 reflect package的主要功能、使用方法和常見用法。

主要功能

reflect package 主要用來在運行時檢查和操作變數的type訊息。這對於需要在不確定type的情況下處理資料的情況非常有用。要使用reflect package,首先需要import它:

import "reflect"

reflect package的主要功能包括:

Type reflect

reflect 可以讓你取得變數的type訊息,方便我們在運行時進行type比較,檢查變數的type。下面是一些基本的type reflect操作:

  • reflect.TypeOf:取得變數的type。
  • reflect.ValueOf:取得變數的value。
  • reflect.Zero:建立一個zero value。
package main

import (
"fmt"
"reflect"
)
func main() {
var num int
typ := reflect.TypeOf(num)
val := reflect.ValueOf(num)
zeroVal := reflect.Zero(typ)

fmt.Printf("Type: %v\n", typ) // Type: int
fmt.Printf("Value: %v\n", val) // Value: 0
fmt.Printf("Zero Value: %v\n", zeroVal) // Zero Value: 0
}

Struct reflect

reflect 可以讓你取得struct欄位的訊息,訪問struct欄位的value,以及修改struct欄位的value。

  • reflect.Value.Field:取得struct欄位的value。
  • reflect.Value.FieldByName:根據欄位名稱取得struct欄位的value。
  • reflect.Value.FieldByIndex:根據欄位索引取得struct欄位的value。
  • reflect.Value.FieldByNameFunc:使用自定義函數查找欄位。
  • reflect.Value.Set:設定變數的value。
package main

import (
"fmt"
"reflect"
)
type Person struct {
Name string
Age int
}
func main() {
person := Person{Name: "Alan", Age: 30}
val := reflect.ValueOf(person)

nameField := val.FieldByName("Name")
fmt.Printf("Name: %v\n", nameField) // Name: "Alan"

ageField := val.FieldByName("Age")
fmt.Printf("Age: %v\n", ageField) // Age: 30

val.FieldByName("Age").SetInt(31)
fmt.Printf("Updated Age: %v\n", person.Age) // Updated Age: 31
}

方法 reflect

reflect 也可以調用struct的方法。

  • reflect.Value.Method:取得struct方法。
  • reflect.Value.MethodByName:根據方法名取得struct方法。
  • reflect.Value.Call:調用方法。
package main

import (
"fmt"
"reflect"
)
type Calculator struct{}

func (c Calculator) Add(a, b int) int {
return a + b
}
func main() {
calculator := Calculator{}

// 使用 reflect.ValueOf 取得計算器物件的reflect value
val := reflect.ValueOf(calculator)

// 使用 MethodByName 方法取得名為 "Add" 的方法的reflect value
method := val.MethodByName("Add")

// 準備方法的參數
args := []reflect.Value{reflect.ValueOf(5), reflect.ValueOf(3)}

// 調用方法並取得結果,然後轉換為整數型別
result := method.Call(args)[0].Interface().(int)

fmt.Printf("Result: %d\n", result) // Result: 8
}

建立新的value

reflect 也可以用來建立一個新的變數,並設定它的value。先建立變數的類型,然後使用 reflect.New 方法來建立,最後使用 Elem 方法取得可設定value的對象:

package main

import (
"fmt"
"reflect"
)
func main() {
// 取得整數類型的 reflect.Type
intType := reflect.TypeOf(0)

// 建立一個新的整數變數
newInt := reflect.New(intType).Elem()

// 設定變數的value
newInt.SetInt(42)

// 取得變數的value
fmt.Println("New Integer Value:", newInt.Int()) // 42
}

修改變數的value

要使用 reflect 修改變數的value,有以下步驟:

  1. 使用 reflect.ValueOf 函數取得變數的 reflect.Value
  2. 使用 .Elem() 方法取得可寫的value(如果原始value是pointer或interface)。
  3. 使用 CanSet 方法確保value可寫。
  4. 使用 SetXXX 方法設定新的value,其中 XXX 是所需的資料type。
package main

import (
"fmt"
"reflect"
)
func main() {
// 創建一個整數變數
var num int = 42

// 使用 reflect.ValueOf 取得 reflect.Value
valueOfNum := reflect.ValueOf(&num).Elem()

// 使用 Elem() 取得可寫的value
if valueOfNum.CanSet() {
// 使用 SetInt 設定新的整數value
valueOfNum.SetInt(99)
}

fmt.Println("Updated Value:", num) // 99
}

在上面的範例中,我們首先使用 reflect.ValueOf 取得整數變數 numreflect.Value,然後使用 .Elem() 方法取得可寫的value。最後,我們使用 SetInt 方法設定新的整數value。請注意,只有在可寫的value上才能使用 SetXXX 方法,使用前記得使用 CanSet 方法來確保value是可寫的。

檢查是否有效和是否為空

reflect package 還提供了 IsValidIsNil 方法,用來檢查 reflect value是否有效(不為zero value)和是否為nil pointer。這在處理 reflect value時非常有用,可以避免意外的錯誤。

package main

import (
"fmt"
"reflect"
)
func main() {
var num int = 42
value := reflect.ValueOf(num)

if value.IsValid() {
fmt.Println("Value is valid")
}

var ptr *int
ptrValue := reflect.ValueOf(ptr)

if ptrValue.IsValid() {
fmt.Println("Pointer Value is valid")
} else if ptrValue.IsNil() {
fmt.Println("Pointer Value is nil")
}
}

常見用法

檢查Interface的動態type

package main

import (
"fmt"
"reflect"
)
func main() {
var x interface{} = 42
val := reflect.ValueOf(x)

if val.Type() == reflect.TypeOf(0) {
fmt.Printf("Value is an integer: %d\n", val.Int())
} else {
fmt.Printf("Value is not an integer\n")
}
}

遍歷struct的欄位

reflect package 可以用來遍歷struct的欄位,這在某些情況下很有用,例如序列化或檢查struct的欄位屬性:

package main

import (
"fmt"
"reflect"
)
type Person struct {
Name string
Age int
Address string
}
func main() {
p := Person{"Alan", 30, "123 Main St"}
valueOfP := reflect.ValueOf(p)

for i := 0; i < valueOfP.NumField(); i++ {
field := valueOfP.Field(i)
fmt.Printf("Field Name: %s, Field Value: %v\n", valueOfP.Type().Field(i).Name, field.Interface())
}
}

檢查方法是否存在

你可以使用reflect package 檢查struct是否實現了某個方法:

package main

import (
"fmt"
"reflect"
)
type Person struct {
Name string
Age int
}
func (p Person) SayHello() {
fmt.Println("Hello, my name is", p.Name)
}
func main() {
p := Person{"Alan", 30}
valueOfP := reflect.ValueOf(p)

method := valueOfP.MethodByName("SayHello")
if method.IsValid() {
fmt.Println("SayHello method exists")
} else {
fmt.Println("SayHello method does not exist")
}
}

操作切片和映射

reflect package 可以用來動態操作切片和映射的元素:

package main

import (
"fmt"
"reflect"
)
func main() {
slice := []int{1, 2, 3}
mapVal := map[string]int{"a": 1, "b": 2}

sliceValue := reflect.ValueOf(slice)
mapValue := reflect.ValueOf(mapVal)

// 修改切片和映射的元素
sliceValue.Index(0).SetInt(42)
mapValue.SetMapIndex(reflect.ValueOf("b"), reflect.ValueOf(99))

fmt.Println(slice) // [42 2 3]
fmt.Println(mapVal) // map[a:1 b:99]
}

📚參考資料

avatar-img
17會員
83內容數
golang
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
Alan的開發者天地 的其他內容
👨‍💻 簡介 今天的encoding/json package是我日常在開發web時很常用到的package之一,主要是用來將Go struct和 JSON 之間進行轉換。主要功能為資料序列化(marshalling)和反序列化(unmarshalling)。
👨‍💻簡介 要處理日期和時間,就必須知道這個Package -> time,Go提供了內建的timePackage。 今天主要介紹time的功能,包括時間操作、格式化等等,以及常見用法。
👨‍💻簡介 要印出Hello World,就會碰到這個Package -> fmt,今天來簡單整理一下fmt的作用、格式化的控制與常見用法。
👨‍💻簡介 昨天講到Goroutine的橋梁aka傳話筒 — Channel,那要怎麼知道對方有收到訊息,我的紙條有送到對方手上呢? 今天就是要來介紹幾種Goroutine的確定完成工作的幾種方式。
👨‍💻簡介 昨天講到Goroutine有稍微簡單介紹Channel,Channel是Go語言中極為重要的併發通訊機制,它就像是不同goroutines之間的話筒,允許它們安全地傳遞資料和信息。這個強大的工具使得Go語言在處理併發任務時非常優雅和高效。
👨‍💻簡介 在日常生活中,如果能同時做很多事情,效率肯定大大提升,那麼在Go語言中,該如何做到呢,答案就是今天的主角Goroutine了,在Go語言中,讓併發變得簡單的強大工具,今天就是來給他一個快速介紹。
👨‍💻 簡介 今天的encoding/json package是我日常在開發web時很常用到的package之一,主要是用來將Go struct和 JSON 之間進行轉換。主要功能為資料序列化(marshalling)和反序列化(unmarshalling)。
👨‍💻簡介 要處理日期和時間,就必須知道這個Package -> time,Go提供了內建的timePackage。 今天主要介紹time的功能,包括時間操作、格式化等等,以及常見用法。
👨‍💻簡介 要印出Hello World,就會碰到這個Package -> fmt,今天來簡單整理一下fmt的作用、格式化的控制與常見用法。
👨‍💻簡介 昨天講到Goroutine的橋梁aka傳話筒 — Channel,那要怎麼知道對方有收到訊息,我的紙條有送到對方手上呢? 今天就是要來介紹幾種Goroutine的確定完成工作的幾種方式。
👨‍💻簡介 昨天講到Goroutine有稍微簡單介紹Channel,Channel是Go語言中極為重要的併發通訊機制,它就像是不同goroutines之間的話筒,允許它們安全地傳遞資料和信息。這個強大的工具使得Go語言在處理併發任務時非常優雅和高效。
👨‍💻簡介 在日常生活中,如果能同時做很多事情,效率肯定大大提升,那麼在Go語言中,該如何做到呢,答案就是今天的主角Goroutine了,在Go語言中,讓併發變得簡單的強大工具,今天就是來給他一個快速介紹。
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
Thumbnail
文章介紹了作者試唱Gawr Gura的《REFLECT》,並分享了翻唱經驗及挑戰。作者也提供了各種不同語言和風格的翻唱版本,如俄文和韓文版本、cosplay翻唱及台V洛可洛斯特的版本。最後推薦了Gura本人的3D Live演出Jazz版,並表達對Gura唱腔的喜愛。
不知道從什麼時候開始,歌單裡面不再只有米津玄師的歌,不論是單曲還是專輯。
日更Day95/365 #自我覺察 標題出自《花木蘭》插曲《Reflection》,中文是「我的倒影何時才能反映出內心真實的自己」。 這次要來聊聊,為何會在自己的臉書本帳上發文,因為最近被同事點了一下XD,覺得還是要面對一下。 其實,我只是因為懶而已😂,那時沒有想過會寫到現在,
Thumbnail
👨‍💻簡介 在 Go 語言中,reflect package是用來檢查和操作變數的type、value和struct。常見用法有檢察 type、調用方法,以及修改變數的value。今天簡單介紹 reflect package的主要功能、使用方法和常見用法。
受著了Re:flect U的舞台衝擊後所寫的小短篇故事。 平行時空中的Re:vale,萬理存在著觀看著演出的兩人的小故事。
Thumbnail
如果你有一個Reflector嬰兒,你把它包得非常暖和——兩周大——你在滿月的晚上把它帶到外面,就像你在給「狼人洗禮」。然後它就會知道它為什麼會在這個星球上。這是瞭解Reflector的最神奇的事情之一。
Thumbnail
本文逾6200多字。闡述了Reflector(反映者/反射者)的Aura、做決定的策略以及如何正確地生活。
Thumbnail
當我和成年反映者打交道時,我的建議是確保他們自己有一個「電腦程序」,這樣他們就可以生成一個星曆,他們可以自己畫出他們的周期,仔細觀察,看看這個「程序」如何影響他們。換句話說,他們需要瞭解Transit勢流過境場的運作方式以及勢流過境場對他們的影響。
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
Thumbnail
文章介紹了作者試唱Gawr Gura的《REFLECT》,並分享了翻唱經驗及挑戰。作者也提供了各種不同語言和風格的翻唱版本,如俄文和韓文版本、cosplay翻唱及台V洛可洛斯特的版本。最後推薦了Gura本人的3D Live演出Jazz版,並表達對Gura唱腔的喜愛。
不知道從什麼時候開始,歌單裡面不再只有米津玄師的歌,不論是單曲還是專輯。
日更Day95/365 #自我覺察 標題出自《花木蘭》插曲《Reflection》,中文是「我的倒影何時才能反映出內心真實的自己」。 這次要來聊聊,為何會在自己的臉書本帳上發文,因為最近被同事點了一下XD,覺得還是要面對一下。 其實,我只是因為懶而已😂,那時沒有想過會寫到現在,
Thumbnail
👨‍💻簡介 在 Go 語言中,reflect package是用來檢查和操作變數的type、value和struct。常見用法有檢察 type、調用方法,以及修改變數的value。今天簡單介紹 reflect package的主要功能、使用方法和常見用法。
受著了Re:flect U的舞台衝擊後所寫的小短篇故事。 平行時空中的Re:vale,萬理存在著觀看著演出的兩人的小故事。
Thumbnail
如果你有一個Reflector嬰兒,你把它包得非常暖和——兩周大——你在滿月的晚上把它帶到外面,就像你在給「狼人洗禮」。然後它就會知道它為什麼會在這個星球上。這是瞭解Reflector的最神奇的事情之一。
Thumbnail
本文逾6200多字。闡述了Reflector(反映者/反射者)的Aura、做決定的策略以及如何正確地生活。
Thumbnail
當我和成年反映者打交道時,我的建議是確保他們自己有一個「電腦程序」,這樣他們就可以生成一個星曆,他們可以自己畫出他們的周期,仔細觀察,看看這個「程序」如何影響他們。換句話說,他們需要瞭解Transit勢流過境場的運作方式以及勢流過境場對他們的影響。