中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

go語言中反射三定律指的是什么

發布時間:2023-01-28 09:39:35 來源:億速云 閱讀:122 作者:iii 欄目:編程語言

這篇文章主要介紹“go語言中反射三定律指的是什么”,在日常操作中,相信很多人在go語言中反射三定律指的是什么問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”go語言中反射三定律指的是什么”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

反射三定律:1、反射可以將“接口類型變量”轉換為“反射類型對象”,這里反射類型指“reflect.Type”和 “reflect.Value”;2、反射可以將“反射類型對象”轉換為“接口類型變量”;3、如果要修改“反射類型對象”其值必須是“可寫的”。

在反射的世界里,我們擁有了獲取一個對象的類型,屬性及方法的能力。

go語言中反射三定律指的是什么

兩種類型:Type 和 Value

在 Go 反射的世界里,有兩種類型非常重要,是整個反射的核心,在學習 reflect 包的使用時,先得學習下這兩種類型:

  • reflect.Type

  • reflect.Value

它們分別對應著真實世界里的 type 和 value,只不過在反射對象里,它們擁有更多的內容。

從源碼上來看,reflect.Type 是以一個接口的形式存在的

type Type interface {
    Align() int
    FieldAlign() int
    Method(int) Method
    MethodByName(string) (Method, bool)
    NumMethod() int
    Name() string
    PkgPath() string
    Size() uintptr
    String() string
    Kind() Kind
    Implements(u Type) bool
    AssignableTo(u Type) bool
    ConvertibleTo(u Type) bool
    Comparable() bool
    Bits() int
    ChanDir() ChanDir
    IsVariadic() bool
    Elem() Type
    Field(i int) StructField
    FieldByIndex(index []int) StructField
    FieldByName(name string) (StructField, bool)
    FieldByNameFunc(match func(string) bool) (StructField, bool)
    In(i int) Type
    Key() Type
    Len() int
    NumField() int
    NumIn() int
    NumOut() int
    Out(i int) Type
    common() *rtype
    uncommon() *uncommonType
}

而 reflect.Value 是以一個結構體的形式存在,

type Value struct {
    typ *rtype
    ptr unsafe.Pointer
    flag
}

同時它接收了很多的方法(見下表),這里出于篇幅的限制這里也沒辦法一一介紹。

Addr
Bool
Bytes
runes
CanAddr
CanSet
Call
CallSlice
call
Cap
Close
Complex
Elem
Field
FieldByIndex
FieldByName
FieldByNameFunc
Float
Index
Int
CanInterface
Interface
InterfaceData
IsNil
IsValid
IsZero
Kind
Len
MapIndex
MapKeys
MapRange
Method
NumMethod
MethodByName
NumField
OverflowComplex
OverflowFloat
OverflowInt
OverflowUint
Pointer
Recv
recv
Send
send
Set
SetBool
SetBytes
setRunes
SetComplex
SetFloat
SetInt
SetLen
SetCap
SetMapIndex
SetUint
SetPointer
SetString
Slice
Slice3
String
TryRecv
TrySend
Type
Uint
UnsafeAddr
assignTo
Convert

通過上一節的內容(),我們知道了一個接口變量,實際上都是由一 pair 對(type 和 data)組合而成,pair 對中記錄著實際變量的值和類型。也就是說在真實世界里,type 和 value 是合并在一起組成 接口變量的。

而在反射的世界里,type 和 data 卻是分開的,他們分別由 reflect.Type 和 reflect.Value 來表現。

解讀反射的三大定律

Go 語言里有個反射三定律,是你在學習反射時,很重要的參考:

  • Reflection goes from interface value to reflection object.

  • Reflection goes from reflection object to interface value.

  • To modify a reflection object, the value must be settable.

翻譯一下,就是:

  • 反射可以將“接口類型變量” 轉換為“反射類型對象”;

  • 反射可以將 “反射類型對象”轉換為 “接口類型變量”;

  • 如果要修改 “反射類型對象” 其類型必須是 “可寫的”;

第一定律

Reflection goes from interface value to reflection object.

為了實現從接口變量到反射對象的轉換,需要提到 reflect 包里很重要的兩個方法:

  • reflect.TypeOf(i) :獲得接口值的類型

  • reflect.ValueOf(i):獲得接口值的值

這兩個方法返回的對象,我們稱之為反射對象:Type object 和 Value object。

go語言中反射三定律指的是什么

舉個例子,看下這兩個方法是如何使用的?

package main

import (
"fmt"
"reflect"
)

func main() {
    var age interface{} = 25

    fmt.Printf("原始接口變量的類型為 %T,值為 %v \n", age, age)

    t := reflect.TypeOf(age)
    v := reflect.ValueOf(age)

    // 從接口變量到反射對象
    fmt.Printf("從接口變量到反射對象:Type對象的類型為 %T \n", t)
    fmt.Printf("從接口變量到反射對象:Value對象的類型為 %T \n", v)

}

輸出如下

原始接口變量的類型為 int,值為 25 從接口變量到反射對象:Type對象的類型為 *reflect.rtype 
從接口變量到反射對象:Value對象的類型為 reflect.Value 復制代碼

如此我們完成了從接口類型變量到反射對象的轉換。

等等,上面我們定義的 age 不是 int 類型的嗎?第一法則里怎么會說是接口類型的呢?

關于這點,其實在上一節(關于接口的三個 『潛規則』)已經提到過了,由于 TypeOf 和 ValueOf 兩個函數接收的是 interface{} 空接口類型,而 Go 語言函數都是值傳遞,因此Go語言會將我們的類型隱式地轉換成接口類型。

原始接口變量的類型為 int,值為 25 
從接口變量到反射對象:Type對象的類型為 *reflect.rtype 
從接口變量到反射對象:Value對象的類型為 reflect.Value

第二定律

Reflection goes from reflection object to interface value.

和第一定律剛好相反,第二定律描述的是,從反射對象到接口變量的轉換。

go語言中反射三定律指的是什么

通過源碼可知, reflect.Value 的結構體會接收 Interface 方法,返回了一個 interface{}  類型的變量(注意:只有 Value 才能逆向轉換,而 Type 則不行,這也很容易理解,如果 Type 能逆向,那么逆向成什么呢?

// Interface returns v's current value as an interface{}.
// It is equivalent to:
//    var i interface{} = (v's underlying value)
// It panics if the Value was obtained by accessing
// unexported struct fields.
func (v Value) Interface() (i interface{}) {
    return valueInterface(v, true)
}

這個函數就是我們用來實現將反射對象轉換成接口變量的一個橋梁。

例子如下

package main

import (
"fmt"
"reflect"
)

func main() {
    var age interface{} = 25

    fmt.Printf("原始接口變量的類型為 %T,值為 %v \n", age, age)

    t := reflect.TypeOf(age)
    v := reflect.ValueOf(age)

    // 從接口變量到反射對象
    fmt.Printf("從接口變量到反射對象:Type對象的類型為 %T \n", t)
    fmt.Printf("從接口變量到反射對象:Value對象的類型為 %T \n", v)

    // 從反射對象到接口變量
    i := v.Interface()
    fmt.Printf("從反射對象到接口變量:新對象的類型為 %T 值為 %v \n", i, i)

}

輸出如下

原始接口變量的類型為 int,值為 25 
從接口變量到反射對象:Type對象的類型為 *reflect.rtype 
從接口變量到反射對象:Value對象的類型為 reflect.Value 
從反射對象到接口變量:新對象的類型為 int 值為 25

當然了,最后轉換后的對象,靜態類型為 interface{} ,如果要轉成最初的原始類型,需要再類型斷言轉換一下,關于這點,我已經在上一節里講解過了,你可以點此前往復習:()。

i := v.Interface().(int)

至此,我們已經學習了反射的兩大定律,對這兩個定律的理解,我畫了一張圖,你可以用下面這張圖來加強理解,方便記憶。

go語言中反射三定律指的是什么

第三定律

To modify a reflection object, the value must be settable.

反射世界是真實世界的一個『映射』,是我的一個描述,但這并不嚴格,因為并不是你在反射世界里所做的事情都會還原到真實世界里。

第三定律引出了一個 settable (可設置性,或可寫性)的概念。

其實早在以前的文章中,我們就一直在說,Go 語言里的函數都是值傳遞,只要你傳遞的不是變量的指針,你在函數內部對變量的修改是不會影響到原始的變量的。

回到反射上來,當你使用 reflect.Typeof 和 reflect.Valueof 的時候,如果傳遞的不是接口變量的指針,反射世界里的變量值始終將只是真實世界里的一個拷貝,你對該反射對象進行修改,并不能反映到真實世界里。

因此在反射的規則里

  • 不是接收變量指針創建的反射對象,是不具備『可寫性』的

  • 是否具備『可寫性』,可使用 CanSet() 來獲取得知

  • 對不具備『可寫性』的對象進行修改,是沒有意義的,也認為是不合法的,因此會報錯。

package main

import (
    "fmt"
    "reflect"
)

func main() {
    var name string = "Go編程時光"

    v := reflect.ValueOf(name)
    fmt.Println("可寫性為:", v.CanSet())
}

輸出如下

可寫性為: false

要讓反射對象具備可寫性,需要注意兩點

  • 創建反射對象時傳入變量的指針

  • 使用 Elem()函數返回指針指向的數據

完整代碼如下

package main

import (
    "fmt"
    "reflect"
)

func main() {
    var name string = "Go編程時光"
    v1 := reflect.ValueOf(&name)
    fmt.Println("v1 可寫性為:", v1.CanSet())

    v2 := v1.Elem()
    fmt.Println("v2 可寫性為:", v2.CanSet())
}

輸出如下

v1 可寫性為: false
v2 可寫性為: true

知道了如何使反射的世界里的對象具有可寫性后,接下來是時候了解一下如何對修改更新它。

反射對象,都會有如下幾個以 Set 單詞開頭的方法

go語言中反射三定律指的是什么

這些方法就是我們修改值的入口。

來舉個例子

package main

import (
    "fmt"
    "reflect"
)

func main() {
    var name string = "Go編程時光"
    fmt.Println("真實世界里 name 的原始值為:", name)

    v1 := reflect.ValueOf(&name)
    v2 := v1.Elem()

    v2.SetString("Python編程時光")
    fmt.Println("通過反射對象進行更新后,真實世界里 name 變為:", name)
}

輸出如下

真實世界里 name 的原始值為: Go編程時光
通過反射對象進行更新后,真實世界里 name 變為: Python編程時光

到此,關于“go語言中反射三定律指的是什么”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

南丹县| 平度市| 宜章县| 陇西县| 偏关县| 嘉荫县| 祁连县| 河池市| 葵青区| 福安市| 宣城市| 冷水江市| 龙井市| 老河口市| 屏东县| 闻喜县| 黄冈市| 手游| 清水河县| 肇州县| 大丰市| 十堰市| 巴东县| 唐河县| 桐庐县| 长子县| 陆良县| 衡阳市| 霍州市| 阿图什市| 永胜县| 宜宾县| 奉节县| 昆山市| 明星| 宜城市| 保德县| 嘉祥县| 开原市| 沁阳市| 天水市|