Maison >développement back-end >Golang >Quelles sont les trois lois de la réflexion dans le langage go

Quelles sont les trois lois de la réflexion dans le langage go

青灯夜游
青灯夜游original
2023-01-18 16:29:451778parcourir

Trois lois de la réflexion : 1. La réflexion peut convertir les "variables de type interface" en "objets de type réflexion", où le type de réflexion fait référence à "reflect.Type" et "reflect.Value" 2. La réflexion peut convertir le "type de réflexion" ; objets" Convertir en "variable de type interface"; 3. Si vous souhaitez modifier "l'objet de type réflexion", sa valeur doit être "inscriptible".

Quelles sont les trois lois de la réflexion dans le langage go

L'environnement d'exploitation de ce tutoriel : système Windows 7, GO version 1.18, ordinateur Dell G3.

Dans le monde de la réflexion, nous avons la capacité d'obtenir le type, les propriétés et les méthodes d'un objet.

Quelles sont les trois lois de la réflexion dans le langage go

Deux types : Type et Valeur

Dans le monde de la réflexion Go, il existe deux types qui sont très importants et sont au cœur de toute la réflexion. Lorsque vous apprenez à utiliser le package de réflexion, vous devez d'abord. apprenez ces deux Types :

  • reflect.Type

  • reflect.Value

Ils correspondent respectivement au type et à la valeur dans le monde réel, mais dans les objets de réflexion, ils ont plus de contenu.

D'après le code source, réflexion.Type existe sous la forme d'une interface

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
}

et réflexion.Value existe sous la forme d'une structure,

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

En même temps, elle reçoit beaucoup de méthodes (voir le tableau ci-dessous), en raison du manque de place, il n'est pas possible de les présenter un par un ici.

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

Grâce au contenu de la section précédente (), nous savons qu'une variable d'interface est en fait composée d'une paire (type et données), et la paire enregistre la valeur et le type de la variable réelle. Autrement dit, dans le monde réel, le type et la valeur sont combinés pour former des variables d'interface.

Dans le monde de la réflexion, le type et les données sont séparés et ils sont représentés respectivement par Reflect.Type et Reflect.Value.

Interprétation des trois lois de la réflexion

Go Il existe trois lois de la réflexion dans le langage, qui sont une référence très importante lorsque vous apprenez la réflexion :

  • La réflexion va de la valeur d'interface à l'objet de réflexion.

  • La réflexion passe de l'objet de réflexion à la valeur d'interface.

  • Pour modifier un objet de réflexion, la valeur doit être paramétrable. Type d'objet";

La réflexion peut convertir "l'objet de type réflexion" en une "interface. type variable";

  • Si vous souhaitez modifier "l'objet de type réflexion", son type doit être "inscriptible";

  • La première loi
  • La réflexion va de la valeur d'interface à l'objet de réflexion.

Afin de réaliser la conversion des variables d'interface en objets de réflexion, nous devons mentionner deux méthodes très importantes dans le package Reflect :

reflect TypeOf(i) : Obtenez le type de valeur d'interface

reflect. .ValueOf(i) : Obtenez la valeur de l'interface value

  • Les objets renvoyés par ces deux méthodes sont appelés objets de réflexion : Type object et Value object .

  • Par exemple, voyons comment ces deux méthodes sont utilisées ?
  • 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)
    
    }
Le résultat est le suivant

原始接口变量的类型为 int,值为 25 从接口变量到反射对象:Type对象的类型为 *reflect.rtype 
从接口变量到反射对象:Value对象的类型为 reflect.Value 复制代码
De cette façon, nous avons terminé la conversion des variables de type d'interface en objets de réflexion.

Quelles sont les trois lois de la réflexion dans le langage goAttendez une minute, l'âge que nous avons défini ci-dessus n'est-il pas de type int ? Comment se fait-il qu’il s’agisse d’un type d’interface dans la première règle ?

Concernant ce point, en effet, il a été évoqué dans la section précédente (

Trois "règles cachées" sur les interfaces

), car les deux fonctions TypeOf et ValueOf reçoivent le type d'interface vide interface{}, et les fonctions du langage Go sont tous transmis par valeur, donc le langage Go convertira implicitement nos types en types d'interface.

原始接口变量的类型为 int,值为 25 
从接口变量到反射对象:Type对象的类型为 *reflect.rtype 
从接口变量到反射对象:Value对象的类型为 reflect.Value

Deuxième loi

La réflexion passe de l'objet de réflexion à la valeur d'interface.

Juste à l'opposé de la première loi, la deuxième loi décrit la conversion de l'objet de réflexion en variable d'interface.

On peut voir à partir du code source que la structure de Reflect.Value recevra des variables de type

(
Remarque : seule la valeur peut être convertie de manière inverse, mais pas le type. Ceci est également facile à comprendre. Si Type peut être inversé, en quoi peut-il être inversé ? Quoi ?

)

// 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)
}

Cette fonction est un pont que nous utilisons pour convertir des objets de réflexion en variables d'interface. Quelles sont les trois lois de la réflexion dans le langage go

Les exemples sont les suivants

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)

至此,我们已经学习了反射的两大定律,对这两个定律的理解,我画了一张图,你可以用下面这张图来加强理解,方便记忆。

Quelles sont les trois lois de la réflexion dans le langage 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 单词开头的方法

Quelles sont les trois lois de la réflexion dans le langage 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视频教程编程教学

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn