Heim >Backend-Entwicklung >Golang >Ein Artikel, der die Reflexion in Golang im Detail erklärt
In diesem Artikel geht es hauptsächlich um GolangMedium Reflection, in der Hoffnung, ein neues Verständnis für Sie zu gewinnen.
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
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 ReflexionAchten Sie zunächst auf die drei wichtigen Gesetze der Reflexion
wird es kein Problem geben, wenn wir nach den Regeln spielenWir 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
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 包中提供的 TypeOf 和 ValueOf 函数得到
其中 reflect.Type
实际上是一个 interface ,他里面包含了各种接口需要进行实现,它里面提供了关于类型相关的信息
其中如下图可以查看到 reflect.Type
的所有方法,其中
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 代码,传入 TypeOf 和 ValueOf 的变量实际上也是一个拷贝,那么如果期望在反射类型的对象中修改其值,那么就需要拿到具体变量的地址然后再进行修改,前提是这个变量是可写的
举个例子你就能明白
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
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) }🎜🎜
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 }🎜🎜
// 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 的接口中涉及的数据结构
在 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 的源码中,我们可以看到,重要的是 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.go
的 func (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!