Heim  >  Artikel  >  Backend-Entwicklung  >  Ein Artikel, der die Reflexion in Golang im Detail erklärt

Ein Artikel, der die Reflexion in Golang im Detail erklärt

青灯夜游
青灯夜游nach vorne
2022-12-14 20:26:346133Durchsuche

In diesem Artikel geht es hauptsächlich um GolangMedium Reflection, in der Hoffnung, ein neues Verständnis für Sie zu gewinnen.

Ein Artikel, der die Reflexion in Golang im Detail erklärt

Obwohl viele Menschen die Go-Sprache schon seit einer gewissen Zeit verwenden und einige sie sogar seit 1 oder 2 Jahren verwenden, sind sie sich immer noch nicht sicher, was die Reflexion in der Go-Sprache angeht, und sie sind nicht sehr sicher wenn man es benutzt. [Verwandte Empfehlungen: Go-Video-Tutorial, Programmierunterricht]

Außerdem wird fast keine Reflexion verwendet. Natürlich ist es am einfachsten und effizientesten, es bei der Arbeit zu verwenden ist skalierbar und bietet eine gute Leistung. Es ist nicht nötig, einige fortgeschrittene Anwendungen mechanisch zu kopieren. Wir können nicht viel ausprobieren von uns selbst. Lassen Sie uns dieses Mal einen genaueren Blick darauf werfen, wie man mit Reflexion spielt.

Artikel Lassen Sie uns darüber sprechen Fälle und flexible Anwendung

  • Reflexionsprinzip
  • Zusammenfassung
  • Einfach gesagt ist Reflexion das, was
  • Einfach ausgedrückt:
  • Reflexion ist die Fähigkeit, auf das Programm selbst zuzugreifen und es zu ändern, während das Programm läuft
. Wenn das Programm beispielsweise ausgeführt wird, können Sie die Feldnamen und Feldwerte des Programms ändern und dem Programm auch Schnittstellenzugriff gewähren Informationen usw.
  • Dies ist ein Mechanismus, der in der Go-Sprache bereitgestellt wird . Wir können viel über die Verwendung von Reflect in der öffentlichen Bibliothek der Go-Sprache sehen. Aber warum verwenden wir im Allgemeinen Reflexion?

Entsprechend der Reflexionsfähigkeit ist der spezifische Datentyp natürlich nur bekannt, wenn das Programm ausgeführt wird, da die von uns bereitgestellte Schnittstelle den eingehenden Datentyp nicht kennt. Aber wir erwarten ihn beim Codieren. Um zu überprüfen, was Der Typ, der beim Ausführen des Programms übergeben wird (z. B. JSON-Serialisierung) und mit diesen spezifischen Daten arbeitet, muss zu diesem Zeitpunkt die Reflexionsfähigkeit nutzen

Es ist also nicht seltsam, dass Sie bei der Verwendung die Schnittstelle sehen können {} Wo spiegelt es sich wider?

Das liegt daran, dass wir nicht sicher sind, um welchen Typ der eingehenden Daten es sich handeln wird. Deshalb haben wir ihn als Schnittstelle konzipiert{}. Wenn Sie sich über die Eigenschaften und die Verwendung der Schnittstelle nicht sicher sind, können Sie die historischen Artikel lesen:

Was sollte ich bei der Schnittstelle{} beachten?

Was sollte ich bei der Schnittstelle{} beachten? Als nächstes

Achten Sie zunächst auf die Regeln der Reflexion

Achten Sie zunächst auf die drei wichtigen Gesetze der Reflexion

wird es kein Problem geben, wenn wir nach den Regeln spielen

Wir kennen die Regeln nicht, es treten immer seltsame Probleme auf, wenn die Klausel ausgelöst wird.

Reflection kann Variablen vom Schnittstellentyp in Objekte vom Reflexionstyp konvertieren Wenn Sie ein Objekt vom Typ „Reflexion“ zur Laufzeit ändern möchten, muss der Wert, der diesem Objekt entspricht, beschreibbar sein. Erinnern Sie sich auch an die oben genannten Zeiger im unsicheren Paket? Wir konvertieren unsere häufig verwendeten Datentypen in bestimmte Datentypen in Paketen (z. B. unsichere Pakete oder Reflect-Pakete) und ändern dann die Daten gemäß den Regeln in den Paketen.

Es entspricht dem Wechseln einer Weste, und Sie können es Mach es Verschiedene Operationen

  • Achten Sie auf die Anwendungsfälle und nutzen Sie sie flexibel
  • Im Allgemeinen verstehen wir zuerst die grundlegende Anwendung, studieren dann ihre Prinzipien und warum sie auf diese Weise verwendet werden kann, und langsam können wir sie verstehen tiefer gehen
  • Für Gesetz 1 konvertieren Sie Schnittstellentypvariablen in Reflexionstypobjekte
  • Tatsächlich können wir für die hier erwähnten Schnittstellentypvariablen Variablen jedes Datentyps übergeben, z. B.
usw.

Unter

Reflection-Typobjekten können hier die Objekte reflect.Type und reflect.Value im Reflect-Paket verstanden werden, auf die über die in bereitgestellten reflect.Type reflect.Value 对象,可以通过 reflect 包中提供的 TypeOfValueOf 函数得到

其中 reflect.Type 实际上是一个 interface ,他里面包含了各种接口需要进行实现,它里面提供了关于类型相关的信息

其中如下图可以查看到 reflect.Type 的所有方法,其中

  • 绿色的 是所有数据类型都是可以调用的
  • 红色的是 函数类型数据可以调用的
  • 黑色的是 Map,数组 Array,通道 Chan,指针 Ptr 或者 切片Slice 可以调用的
  • 蓝色的是结构体调用的
  • 黄色的是通道 channel 类型调用的

reflect.Value 实际上是一个 struct,根据这个 struct 还关联了一组方法,这里面存放了数据类型和具体的数据,通过查看其数据结构就可以看出

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

看到此处的 unsafe.Pointer 是不是很熟悉,底层自然就可以将 unsafe.Pointer 转换成 uintptr,然后再修改其数据后,再转换回来,对于 Go 指针不太熟悉的可以查看这篇文章:

写一个简单的 demo 就可以简单的获取到变量的数据类型和值

func main() {   var demoStr string = "now reflect"
   fmt.Println("type:", reflect.TypeOf(demoStr))
   fmt.Println("value:", reflect.ValueOf(demoStr))
}

对于定律二,将 反射类型的对象 转换成 接口类型的变量

我们可以通过将 reflect.Value 类型转换成我们具体的数据类型,因为 reflect.Value 中有对应的 typ *rtype 以及 ptr unsafe.Pointer

例如我们可以 通过 reflect.Value 对象的 interface() 方法来处理

func main() {   var demoStr string = "now reflect"
   fmt.Println("type:", reflect.TypeOf(demoStr))
   fmt.Println("value:", reflect.ValueOf(demoStr))   var res string
   res = reflect.ValueOf(demoStr).Interface().(string)
   fmt.Println("res == ",res)
}

对于定律三,修改反射类型的对象

首先我们看上书的 demo 代码,传入 TypeOfValueOf 的变量实际上也是一个拷贝,那么如果期望在反射类型的对象中修改其值,那么就需要拿到具体变量的地址然后再进行修改,前提是这个变量是可写的

举个例子你就能明白

func main() {
   var demoStr string = "now reflect"
   v := reflect.ValueOf(demoStr)
   fmt.Println("is canset ", v.CanSet())
   //v.SetString("hello world")   // 会panic
   }

可以先调用 reflect.Value 对象的 CanSet 查看是否可写,如果是可写的,我们再写,如果不可写就不要写了,否则会 panic

那么传入变量的地址就可以修改了??

传入地址的思路没有毛病,但是我们去设置值的方式有问题,因此也会出现上述的 panic 情况

此处仔细看能够明白,反射的对象 v 自然是不可修改的,我们应该找到 reflect.Value 里面具体具体的数据指针,那么才是可以修改的,可以使用 reflect.Value TypeOf und zugegriffen werden kann Das Reflect-Paket. Die ValueOf-Funktion erhält

wobei reflect.Type tatsächlich eine Schnittstelle ist, die verschiedene Schnittstellen enthält, die implementiert werden müssen. Sie stellt Informationen über den Typ bereit, die angezeigt werden können Wie in der Abbildung unten gezeigt. Zu allen Methoden von reflect.Type gehören die

grünen, bei denen alle Datentypen aufgerufen werden können.

  • rote
  • sind die Funktionstypdaten, die aufgerufen werden können aufgerufen werden
  • Die schwarzen sind Map, Array Array, Channel Chan, Pointer Ptr oder Slice Slice, die aufgerufen werden können
Blue

werden durch die Struktur

🎜🎜🎜Gelb 🎜 aufgerufen nach Kanaltyp 🎜🎜🎜🎜 🎜reflect.Value ist eigentlich eine Struktur, der auch eine Reihe von Methoden zugeordnet sind, die den Datentyp und spezifische Daten speichern. Sie können dies anhand der Datenstruktur erkennen 🎜Siehe hier: Sind Sie mit unsafe.Pointer vertraut? Die unterste Ebene kann unsafe.Pointer natürlich in uintptr konvertieren, dann seine Daten ändern und sie dann wieder in For Go-Zeiger konvertieren , das ist es nicht. Wenn Sie zu vertraut sind, können Sie sich diesen Artikel ansehen: 🎜🎜🎜Zeiger in GO? 🎜🎜🎜Schreiben Sie eine einfache Demo, um den Datentyp und den Wert der Variablen einfach zu erhalten🎜
type RDemo struct {
   Name  string
   Age   int
   Money float32
   Hobby map[string][]string
}

func main() {
   tmp := &RDemo{
      Name:  "xiaomiong",
      Age:   18,
      Money: 25.6,
      Hobby: map[string][]string{
         "sport": {"basketball", "football"},
         "food":  {"beef"},
      },
   }

   v := reflect.ValueOf(tmp).Elem()  // 拿到结构体对象
   h := v.FieldByName("Hobby")    // 拿到 Hobby 对象
   h1 := h.MapKeys()[0]    // 拿到 Hobby 的第 0 个key
   fmt.Println("key1 name == ",h1.Interface().(string))

   sli := h.MapIndex(h1)    // 拿到 Hobby 的第 0 个key对应的对象
   str := sli.Index(1)      // 拿到切片的第 1 个对象
   fmt.Println(str.CanSet())

   str.SetString("helloworld")
   fmt.Println("tmp == ",tmp)
}
🎜🎜

🎜Konvertieren Sie für Gesetz 2 Objekte vom Reflexionstyp in Variablen vom Schnittstellentyp.🎜🎜 🎜Wir können die konvertieren reflect.Value Typ in unseren spezifischen Datentyp, da es einen entsprechenden typ *rtype in reflect.Value und ptr unsicher gibt. Zeiger🎜🎜Zum Beispiel können wir ihn über die interface()-Methode des reflect.Value-Objekts verarbeiten🎜🎜🎜
// rtype must be kept in sync with ../runtime/type.go:/^type._type.
type rtype struct {
   size       uintptr
   ptrdata    uintptr
   hash       uint32 
   tflag      tflag
   align      uint8
   fieldAlign uint8
   kind       uint8
   equal     func(unsafe.Pointer, unsafe.Pointer) bool
   gcdata    *byte 
   str       nameOff
   ptrToThis typeOff
}
🎜🎜

Ändern Sie für Gesetz drei das Reflexionstypobjekt 🎜🎜Zuerst betrachten wir Im Democode im Buch handelt es sich bei den in 🎜TypeOf🎜 und 🎜ValueOf🎜 übergebenen Variablen tatsächlich um Kopien. Wenn Sie also seinen Wert in einem Reflexionstypobjekt ändern möchten, müssen Sie die Adresse der spezifischen Variablen abrufen und diese dann ändern . 🎜Die Voraussetzung ist, dass diese Variable beschreibbar ist🎜🎜🎜Sie können es anhand eines Beispiels verstehen🎜
// emptyInterface is the header for an interface{} value.type emptyInterface struct {
   typ  *rtype
   word unsafe.Pointer
}复制代码
🎜🎜🎜Sie können zuerst das CanSet des reflect.Value -Objekts aufrufen, um zu überprüfen, ob es beschreibbar ist. Wenn ja , Lass es uns noch einmal schreiben. Wenn es nicht geschrieben werden kann, schreibe es nicht, sonst gerät es in Panik🎜🎜🎜🎜Dann kann die Adresse der übergebenen Variablen geändert werden? ? 🎜🎜🎜🎜Eingehende Adresse Es gibt An der Idee ist nichts auszusetzen, aber an der Art und Weise, wie wir den Wert festlegen, stimmt etwas nicht, sodass auch hier die oben erwähnte Paniksituation auftritt. Wenn Sie hier genau hinschauen, können Sie verstehen, dass das reflektierte Objekt v natürlich nicht veränderbar ist reflect.Value Der spezifische Datenzeiger in kann für einen etwas komplizierteren Fall geändert werden , Sie denken vielleicht, dass es sich um einen so einfachen Fall handelt und dass er in Ordnung sein wird, sobald er demonstriert wird, aber er wird abstürzen, sobald er bei der Arbeit verwendet wird. Natürlich ist er noch nicht vollständig verstanden, was bedeutet, dass dies der Fall ist Es wurde nicht gut verdaut. Hier ist ein weiteres Beispiel bei der Arbeit: Eine Struktur hat eine Karte, der Schlüssel in der Karte ist ein String und der Wert ist [] String. Die Anforderung besteht darin, auf den 🎜1🎜 zuzugreifen Element des Slice, das dem Hobby-Feld in der Struktur entspricht, mit dem Kartenschlüssel 🎜sport🎜 und ändern Sie es in 🎜 hellolworld🎜🎜🎜rrreee🎜🎜🎜

可以看到上述案例运行之后有时可以运行成功,有时会出现 panic 的情况,相信细心的 xdm 就可以看出来,是因为 map 中的 key 是 无序的导致的,此处也提醒一波,使用 map 的时候要注意这一点

看上述代码,是不是就能够明白咱们使用反射去找到对应的数据类型,然后按照数据类型进行处理数据的过程了呢

有需要的话,可以慢慢的去熟练反射包中涉及的函数,重点是要了解其三个规则,对象转换方式,访问方式,以及数据修改方式

反射原理

那么通过上述案例,可以知道关于反射中数据类型和数据指针对应的值是相当重要的,不同的数据类型能够用哪些函数这个需要注意,否则用错直接就会 panic

TypeOf

来看 TypeOf 的接口中涉及的数据结构

在 reflect 包中 rtype 是非常重要的,Go 中所有的类型都会包含这个结构,所以咱们反射可以应用起来,结构如下

// rtype must be kept in sync with ../runtime/type.go:/^type._type.
type rtype struct {
   size       uintptr
   ptrdata    uintptr
   hash       uint32 
   tflag      tflag
   align      uint8
   fieldAlign uint8
   kind       uint8
   equal     func(unsafe.Pointer, unsafe.Pointer) bool
   gcdata    *byte 
   str       nameOff
   ptrToThis typeOff
}

其中可以看到此处的 rtype 的结构保持和 runtime/type.go 一致 ,都是关于数据类型的表示,以及对应的指针,关于这一块的说明和演示可以查看文末的 interface{} 处的内容

ValueOf

ValueOf 的源码中,我们可以看到,重要的是 emptyInterface 结构

// emptyInterface is the header for an interface{} value.type emptyInterface struct {
   typ  *rtype
   word unsafe.Pointer
}复制代码

emptyInterface 结构中有 rtype 类型的指针, word 自然是对应的数据的地址了

reflect.Value 对象中的方法也是非常的多,用起来和上述说到的 reflect.Type 接口中的功能类似

关于源码中涉及到的方法,就不再过多的赘述了,更多的还是需要自己多多实践才能体会的更好

殊不知,此处的 reflect.Value 也是可以转换成 reflect.Type ,可以查看源码中 reflect\value.gofunc (v Value) Type() Type {

其中   reflect.Value  ,reflect.Type ,和任意数据类型 可以相互这样来转换

如下图:

总结

至此,关于反射就聊到这里,一些关于源码的细节并没有详细说,更多的站在一个使用者的角度去看反射需要注意的点

关于反射,大多的人是建议少用,因为是会影响到性能,不过如果不太关注这一点,那么用起来还是非常方便的

高级功能自然也是双刃剑,你用不好就会 panic,如果你期望去使用他,那么就去更多的深入了解和一步一步的吃透他吧

大道至简,反射三定律,活学活用

更多编程相关知识,请访问:编程视频!!

Das obige ist der detaillierte Inhalt vonEin Artikel, der die Reflexion in Golang im Detail erklärt. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:juejin.cn. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen