Heim  >  Artikel  >  Backend-Entwicklung  >  Was nützt die Golang-Reflexion?

Was nützt die Golang-Reflexion?

青灯夜游
青灯夜游Original
2020-04-17 14:30:362804Durchsuche

Was nützt die Golang-Reflexion? Der folgende Artikel führt Sie in die Golang-Reflexion ein. Es hat einen gewissen Referenzwert. Freunde in Not können sich darauf beziehen. Ich hoffe, es wird für alle hilfreich sein.

Was nützt die Golang-Reflexion?

golang (go) ist eine prozedurale Programmiersprache, die für die schnelle Kompilierung von Maschinencode verwendet werden kann. Es handelt sich um eine statisch typisierte kompilierte Sprache. Es bietet einen Parallelitätsmechanismus, der die Entwicklung von Multicore- und Netzwerkprogrammen auf Maschinenebene erleichtert. Es handelt sich um eine schnelle, dynamisch typisierte und interpretierte Sprache; sie bietet Unterstützung für Schnittstellen und Typeinbettung.

Grundlegendes Verständnis

In der Go-Sprache ist der Wert/Typ/Funktion meist sehr einfach, wenn Sie möchten, definieren Sie einen . Sie wollen eine Struktur

type Foo struct {
    A int 
    B string
}

Sie wollen einen Wert, Sie definieren ihn

var x Foo

Sie wollen eine Funktion, Sie definieren sie

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

Aber manchmal müssen Sie etwas tun, das es kann kann nur zur Laufzeit ermittelt werden, z. B. beim Abrufen einiger Wörterbuchdaten aus einer Datei oder dem Netzwerk. Oder vielleicht möchten Sie verschiedene Arten von Daten erhalten. In diesem Fall ist reflection(反射) praktisch. Reflection ermöglicht Ihnen die folgenden Funktionen:

  • Typ zur Laufzeit prüfen

  • Zur Laufzeit prüfen/ändern/erstellen Werte/Funktionen/Strukturen

Im Allgemeinen dreht sich gos reflection um drei Konzepte Types, Kinds, Values. Alle Operationen zur Reflexion sind im reflectPaket

Reflection Power

Type Power

Schauen wir uns zunächst an, wie man den Werttyp durch Reflektion erhält.

varType := reflect.TypeOf(var)

Auf der Reflexionsoberfläche können Sie sehen, dass viele Funktionen darauf warten, von uns genutzt zu werden. Ihr könnt es in den Kommentaren sehen. Das Reflection-Paket geht davon aus, dass wir wissen, was wir tun werden. Beispielsweise wird varType.Elem() in Panik geraten. Denn Elem() hat diese Methode nur für Array, Chan, Map, Ptr oder Slice. Diese Typen haben nur diese Methode. Weitere Informationen finden Sie im Testcode. Sie können Beispiele für alle Reflect-Funktionen sehen, indem Sie den folgenden Code ausführen

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

Zusätzlich zur Überprüfung des Typs einer Variablen können Sie lesen/schreiben /erstelle einen neuen durch Reflexionswert. Holen Sie sich aber zunächst den Reflexionswerttyp

refVal := reflect.ValueOf(var)

, wenn Sie den Wert der Variablen ändern möchten. Sie müssen den Reflexionszeiger erhalten, der auf die Variable zeigt. Der genaue Grund wird später erklärt

refPtrVal := reflect.ValueOf(&var)

Natürlich haben Sie „reflect.Value“ und können „reflect.Type“ einfach über die Type()-Methode erhalten. Wenn Sie den Wert der Variablen ändern möchten, verwenden Sie

refPtrVal.Elem().Set(newRefValue)

. Natürlich müssen die Parameter der Set-Methode auch „reflect.Value“ sein , verwenden Sie den folgenden Code

newPtrVal := reflect.New(varType)

Dann verwenden Sie Elem().Set(), um den Wert zu initialisieren. Natürlich gibt es eine Reihe verschiedener Methoden für unterschiedliche Werte. Ich werde es hier nicht aufschreiben. Konzentrieren wir uns auf das folgende offizielle Beispiel

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

Prinzip

Typ und Schnittstelle verstehen

Los ist eine statisch typisierte Sprache, z. B. int, float. Was ist ein statischer Typ? Beispielsweise bedeutet derselbe Binärblock 00000001 im Typ „bool“ wahr. Die Erklärung im int-Typ ist 1. Schauen wir uns das folgende einfachste Beispiel an:

type MyInt int
var i int
var j MyInt

i,j werden im Speicher durch den zugrunde liegenden Typ int dargestellt, im eigentlichen Codierungsprozess jedoch nicht beim Kompilieren vom gleichen Typ, und Sie können den Wert von i nicht direkt j zuweisen. Ist das nicht etwas seltsam? Wenn Sie es ausführen, wird Ihnen der Compiler mitteilen, dass Sie einem Wert vom Typ int keinen Wert vom Typ MyInt zuweisen können. Dieser Typ ist weder eine Klasse noch ein Python-Typ.

Schnittstelle ist ein spezieller Typ, der eine Sammlung von Methoden darstellt. Ein Schnittstellenwert kann jeden bestimmten Wert speichern, solange der Wert die Schnittstellenmethode implementiert. interface{} ähnelt manchmal dem Objekt von Java. Tatsächlich besteht die Schnittstelle aus zwei Teilen, dem tatsächlichen Wert und dem spezifischen Typ des Werts. Dies kann auch erklären, warum sich der folgende Code von anderen Sprachen unterscheidet. Einzelheiten zu den Schnittstellenprinzipien finden Sie unter go data structure: 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)
}

Drei Reflexionssätze von Golang

Teilen Sie einen Schnittstellenwert in ein Reflexionsobjekt auf

Die Reflexion wird nur zur Überprüfung des Schnittstellenwerts (Wert, Typ) verwendet. Wie im vorherigen Kapitel erwähnt, sind die beiden Methoden ValueOf und TypeOf. Über ValueOf können wir Type

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

erhalten. Dieser Code gibt

type: float64

aus. Deklarieren Sie einfach eine float64-Variable. Woher kommt die Schnittstelle? Ja, die Antwort ist im TypeOf-Parameter versteckt

func TypeOf(i interface{}) Type

Wenn wir „reflect.TypeOf(x)“ aufrufen, wird x zunächst in einer leeren Schnittstelle gespeichert. Anschließend wird es als Parameter an den Funktionsausführungsstapel übergeben. ** Reflect.TypeOf entpackt das Paar dieser Schnittstelle und stellt die Typinformationen wieder her**

Kombinieren Sie das Reflexionsobjekt zu einem Schnittstellenwert

就像镜面反射一样,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教程

Das obige ist der detaillierte Inhalt vonWas nützt die Golang-Reflexion?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn