Maison  >  Article  >  développement back-end  >  A quoi sert la réflexion golang ?

A quoi sert la réflexion golang ?

青灯夜游
青灯夜游original
2020-04-17 14:30:362850parcourir

A quoi sert la réflexion golang ? L'article suivant vous présentera la réflexion golang. Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer. J'espère qu'il sera utile à tout le monde.

A quoi sert la réflexion golang ?

golang (go) est un langage de programmation procédural qui peut être utilisé pour une compilation rapide de code machine. C'est un langage compilé à typage statique. Il fournit un mécanisme de concurrence qui facilite le développement de programmes multicœurs et en réseau au niveau machine. Il s'agit d'un langage rapide, typé et interprété de manière dynamique ; il prend en charge les interfaces et l'intégration de types.

Compréhension de base

En langage Go, la plupart du temps la valeur/type/fonction est très simple, si vous le souhaitez, définissez-en une . Vous voulez une Struct

type Foo struct {
    A int 
    B string
}

Vous voulez une valeur, vous la définissez

var x Foo

Vous voulez une fonction, vous la définissez

func DoSomething(f Foo) {
  fmt.Println(f.A, f.B)
}

Mais parfois, vous devez faire quelque chose qui peut ne peut être déterminé qu'au moment de l'exécution, par exemple pour obtenir des données de dictionnaire à partir d'un fichier ou du réseau. Ou peut-être souhaitez-vous obtenir différents types de données. Dans ce cas, reflection(反射) est utile. Reflection vous permet d'avoir les fonctionnalités suivantes :

  • Vérifier le type au moment de l'exécution

  • Vérifier/modifier au moment de l'exécution/Créer valeurs/fonctions/structures

En général, les go reflection s'articulent autour de trois concepts Types, Kinds, Values. Toutes les opérations sur la réflexion sont dans le reflect package

Reflection Power

Type Power

Tout d'abord, voyons comment obtenir le type valeur par réflexion.

varType := reflect.TypeOf(var)

Depuis l'interface de réflexion, vous pouvez voir qu'il y a beaucoup de fonctions qui attendent que nous les utilisions. Vous pouvez le voir dans les commentaires. Le package de réflexion suppose que nous savons ce que nous allons faire. Par exemple, varType.Elem() va paniquer. Parce que Elem() n'a cette méthode que pour Array, Chan, Map, Ptr ou Slice, ces types n'ont que cette méthode. Veuillez consulter le code de test pour plus de détails. Vous pouvez voir des exemples de toutes les fonctions de réflexion en exécutant le code suivant

package main
import (
    "fmt"
    "reflect"
)
type FooIF interface {
    DoSomething()
    DoSomethingWithArg(a string)
    DoSomethingWithUnCertenArg(a ... string)
}
type Foo struct {
    A int
    B string
    C struct {
        C1 int
    }
}
func (f *Foo) DoSomething() {
    fmt.Println(f.A, f.B)
}
func (f *Foo) DoSomethingWithArg(a string) {
    fmt.Println(f.A, f.B, a)
}
func (f *Foo) DoSomethingWithUnCertenArg(a ... string) {
    fmt.Println(f.A, f.B, a[0])
}
func (f *Foo) returnOneResult() int {
    return 2
}
func main() {
    var simpleObj Foo
    var pointer2obj = &simpleObj
    var simpleIntArray = [3]int{1, 2, 3}
    var simpleMap = map[string]string{
        "a": "b",
    }
    var simpleChan = make(chan int, 1)
    var x uint64
    var y uint32
    varType := reflect.TypeOf(simpleObj)
    varPointerType := reflect.TypeOf(pointer2obj)
    // 对齐之后要多少容量
    fmt.Println("Align: ", varType.Align())
    // 作为结构体的`field`要对其之后要多少容量
    fmt.Println("FieldAlign: ", varType.FieldAlign())
    // 叫啥
    fmt.Println("Name: ", varType.Name())
    // 绝对引入路径
    fmt.Println("PkgPath: ", varType.PkgPath())
    // 实际上用了多少内存
    fmt.Println("Size: ", varType.Size())
    // 到底啥类型的
    fmt.Println("Kind: ", varType.Kind())
    // 有多少函数
    fmt.Println("NumMethod: ", varPointerType.NumMethod())
    // 通过名字获取一个函数
    m, success := varPointerType.MethodByName("DoSomethingWithArg")
    if success {
        m.Func.Call([]reflect.Value{
            reflect.ValueOf(pointer2obj),
            reflect.ValueOf("sad"),
        })
    }
    // 通过索引获取函数
    m = varPointerType.Method(1)
    m.Func.Call([]reflect.Value{
        reflect.ValueOf(pointer2obj),
        reflect.ValueOf("sad2"),
    })
    // 是否实现了某个接口
    fmt.Println("Implements:", varPointerType.Implements(reflect.TypeOf((*FooIF)(nil)).Elem()))
    //  看看指针多少bit
    fmt.Println("Bits: ", reflect.TypeOf(x).Bits())
    // 查看array, chan, map, ptr, slice的元素类型
    fmt.Println("Elem: ", reflect.TypeOf(simpleIntArray).Elem().Kind())
    // 查看Array长度
    fmt.Println("Len: ", reflect.TypeOf(simpleIntArray).Len())
    // 查看结构体field
    fmt.Println("Field", varType.Field(1))
    // 查看结构体field
    fmt.Println("FieldByIndex", varType.FieldByIndex([]int{2, 0}))
    // 查看结构提field
    fi, success2 := varType.FieldByName("A")
    if success2 {
        fmt.Println("FieldByName", fi)
    }
    // 查看结构体field
    fi, success2 = varType.FieldByNameFunc(func(fieldName string) bool {
        return fieldName == "A"
    })
    if success2 {
        fmt.Println("FieldByName", fi)
    }
    //  查看结构体数量
    fmt.Println("NumField", varType.NumField())
    // 查看map的key类型
    fmt.Println("Key: ", reflect.TypeOf(simpleMap).Key().Name())
    // 查看函数有多少个参数
    fmt.Println("NumIn: ", reflect.TypeOf(pointer2obj.DoSomethingWithUnCertenArg).NumIn())
    // 查看函数参数的类型
    fmt.Println("In: ", reflect.TypeOf(pointer2obj.DoSomethingWithUnCertenArg).In(0))
    // 查看最后一个参数,是否解构了
    fmt.Println("IsVariadic: ", reflect.TypeOf(pointer2obj.DoSomethingWithUnCertenArg).IsVariadic())
    // 查看函数有多少输出
    fmt.Println("NumOut: ", reflect.TypeOf(pointer2obj.DoSomethingWithUnCertenArg).NumOut())
    // 查看函数输出的类型
    fmt.Println("Out: ", reflect.TypeOf(pointer2obj.returnOneResult).Out(0))
    // 查看通道的方向, 3双向。
    fmt.Println("ChanDir: ", int(reflect.TypeOf(simpleChan).ChanDir()))
    // 查看该类型是否可以比较。不能比较的slice, map, func
    fmt.Println("Comparable: ", varPointerType.Comparable())
    // 查看类型是否可以转化成另外一种类型
    fmt.Println("ConvertibleTo: ", varPointerType.ConvertibleTo(reflect.TypeOf("a")))
    // 该类型的值是否可以另外一个类型
    fmt.Println("AssignableTo: ", reflect.TypeOf(x).AssignableTo(reflect.TypeOf(y)))
}

Power of Value

En plus de vérifier le type d'une variable, vous pouvez lire/écrire /créer un nouveau grâce à la valeur de réflexion. Mais obtenez d'abord le type de valeur de réflexion

refVal := reflect.ValueOf(var)

si vous souhaitez modifier la valeur de la variable. Vous devez obtenir le pointeur de réflexion pointant vers la variable. La raison spécifique sera expliquée plus tard

refPtrVal := reflect.ValueOf(&var)

Bien sûr, vous avez Reflect.Value, et vous pouvez facilement obtenir Reflect.Type via la méthode Type(). Si vous souhaitez modifier la valeur de la variable, utilisez

refPtrVal.Elem().Set(newRefValue)

. Bien entendu, les paramètres de la méthode Set doivent également être réfléchis.Value

si vous souhaitez créer une nouvelle valeur. , utilisez le code suivant

newPtrVal := reflect.New(varType)

Ensuite, utilisez Elem().Set() pour initialiser la valeur. Bien sûr, il existe de nombreuses méthodes différentes pour différentes valeurs. Je ne l'écrirai pas ici. Concentrons-nous sur l'exemple officiel suivant

package main
import (
    "fmt"
    "reflect"
)
func main() {
    // swap is the implementation passed to MakeFunc.
    // It must work in terms of reflect.Values so that it is possible
    // to write code without knowing beforehand what the types
    // will be.
    swap := func(in []reflect.Value) []reflect.Value {
        return []reflect.Value{in[1], in[0]}
    }
    // makeSwap expects fptr to be a pointer to a nil function.
    // It sets that pointer to a new function created with MakeFunc.
    // When the function is invoked, reflect turns the arguments
    // into Values, calls swap, and then turns swap's result slice
    // into the values returned by the new function.
    makeSwap := func(fptr interface{}) {
        // fptr is a pointer to a function.
        // Obtain the function value itself (likely nil) as a reflect.Value
        // so that we can query its type and then set the value.
        fn := reflect.ValueOf(fptr).Elem()
        // Make a function of the right type.
        v := reflect.MakeFunc(fn.Type(), swap)
        // Assign it to the value fn represents.
        fn.Set(v)
    }
    // Make and call a swap function for ints.
    var intSwap func(int, int) (int, int)
    makeSwap(&intSwap)
    fmt.Println(intSwap(0, 1))
    // Make and call a swap function for float64s.
    var floatSwap func(float64, float64) (float64, float64)
    makeSwap(&floatSwap)
    fmt.Println(floatSwap(2.72, 3.14))
}

Principe

Comprendre le type et l'interface

Aller est un langage typé statiquement. Chaque variable a un type statique, tel que int, float. Ma compréhension est un bloc binaire d'une certaine longueur et explication. Par exemple, le même bloc binaire 00000001 signifie vrai dans le type booléen. L'explication dans le type int est 1. Jetons un coup d'œil à l'exemple le plus simple suivant

type MyInt int
var i int
var j MyInt

i,j sont représentés par le type sous-jacent int dans la mémoire, mais dans le processus de codage réel, ils ne sont pas de le même type une fois compilé, et vous ne pouvez pas attribuer directement la valeur de i à j. N'est-ce pas un peu étrange ? Lorsque vous l'exécutez, le compilateur vous dira que vous ne pouvez pas attribuer une valeur de type MyInt à une valeur de type int. Ce type n'est ni une classe ni un type python.

l'interface est un type spécial qui représente une collection de méthodes. Une valeur d'interface peut stocker n'importe quelle valeur tant que la valeur implémente la méthode d'interface. interface{} est parfois similaire à l'objet Java. En fait, l'interface se compose de deux parties, la valeur réelle et le type spécifique de la valeur. Cela peut également expliquer pourquoi le code suivant est différent des autres langages. Pour plus de détails sur les principes de l'interface, veuillez vous référer à go data structures : interfaces.

package main
import (
    "fmt"
)
type A interface {
    x(param int)
}
type B interface {
    y(param int)
}
type AB struct {
}
func (ab *AB) x(param int) {
    fmt.Printf("%p", ab)
    fmt.Println(param)
}
func (ab *AB) y(param int) {
    fmt.Printf("%p", ab)
    fmt.Println(param)
}
func printX(a A){
    fmt.Printf("%p", a)
    a.x(2)
}
func printY(b B){
    fmt.Printf("%p", b)
    b.y(3)
}
func main() {
    var ab = new(AB)
    printX(ab)
    printY(ab)
    var aInfImpl A
    var bInfImpl B
    aInfImpl = new(AB)
    //bInfImpl = aInfImpl  会报错
    bInfImpl = aInfImpl.(B)
    bInfImpl.y(2)
}

Trois théorèmes de réflexion de Golang

Diviser une valeur d'interface en un objet de réflexion

La réflexion sert uniquement à vérifier la valeur de l'interface (Valeur, Type). Comme mentionné dans le chapitre précédent, les deux méthodes ValueOf et TypeOf. Grâce à ValueOf, nous pouvons facilement obtenir Type

package main
import (
    "fmt"
    "reflect"
)
func main() {
    var x float64 = 3.4
    fmt.Println("type:", reflect.TypeOf(x))
}

Ce code génère

type: float64

. La question devient alors : où est l'interface ? Déclarez simplement une variable float64. D'où vient l'interface. Oui, la réponse est cachée dans le paramètre TypeOf

func TypeOf(i interface{}) Type

Lorsque nous appelons Reflect.TypeOf(x), x est d'abord stocké dans une interface vide. Ensuite, il est transmis à la pile d’exécution de fonctions en tant que paramètre. **reflect.TypeOf déballe la paire de cette interface et récupère les informations de type**

Combinez l'objet de réflexion en une valeur d'interface

就像镜面反射一样,go的反射是可逆的。给我一个reflect.Value。我们能够恢复出一个interface的值。事实上,以下函数干的事情就是将Value和Type组狠起来塞到 interface里面去。所以我们可以

y := v.Interface().(float64) // y will have type float64.
fmt.Println(y)

接下来就是见证奇迹的时刻。fmt.Println和fmt.Printf的参数都是interface{}。我们真正都不需要将上面例子的y转化成明确的float64。我就可以去打印他比如

fmt.Println(v.Interface())

甚至我们的interface藏着的那个type是float64。我们可以直接用占位符来打印

fmt.Println("Value is %7.le\n", v.Interface())

再重复一边,没有必要将v.Interface()的类型强转到float64;这个空的interface{}包含了concrete type。函数调用会恢复出来

要改变一个反射对象,其值必须是可设置的

第三条比较让你比较困惑。不过如果我们理解了第一条,那么这条其实非常好理解。先看一下下面这个例子

var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1) // Error: will panic.

如果执行这段代码,你会发现出现panic以下信息

panic: reflect.Value.SetFloat using unaddressable value

可设置性是一个好东西,但不是所有reflect.Value都有他...可以通过CanSet 函数来获取是否可设置

var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("settability of v:", v.CanSet())

那么到底为什么要有一个可设置性呢?可寻址才可设置,我们在用reflect.ValueOf时候,实际上是函数传值。获取x的反射对象,实际上是另外一个float64的内存的反射对象。这个时候我们再去设置该反射对象的值,没有意义。这段内存并不是你申明的那个x。

推荐学习:Golang教程

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