Reflexion
Reflexion ist ein Mechanismus zur Überprüfung der eigenen Sprachstruktur zur Laufzeit. Sie ermöglicht flexible Lösungen für bestimmte Probleme, bringt aber auch offensichtliche Nachteile wie Leistungsprobleme mit sich. In Go ist Reflexion eng mit interface{} verbunden. Wo immer interface{} erscheint, gibt es meist auch Reflexion. Die Reflexions-API in Go wird vom Standardpaket reflect bereitgestellt.
Schnittstellen
Bevor wir beginnen, lassen Sie uns kurz zwei Schnittstellen im runtime-Paket betrachten. In Go sind Schnittstellen im Wesentlichen Strukturen. Go teilt Schnittstellen zur Laufzeit in zwei Kategorien: Schnittstellen ohne Methodensatz und Schnittstellen mit Methodensatz. Für Schnittstellen mit Methodensatz wird zur Laufzeit die Struktur iface verwendet:
type iface struct {
tab *itab // Enthält Datentyp, Schnittstellentyp, Methodensatz usw.
data unsafe.Pointer // Zeiger auf den Wert
}Für Schnittstellen ohne Methodensatz wird die Struktur eface verwendet:
type eface struct {
_type *_type // Typ
data unsafe.Pointer // Zeiger auf den Wert
}Beide Strukturen haben entsprechende Strukturtypen im reflect-Paket. iface entspricht nonEmptyInterface:
type nonEmptyInterface struct {
itab *struct {
ityp *rtype // Statischer Schnittstellentyp
typ *rtype // Dynamischer konkreter Typ
hash uint32 // Typ-Hash
_ [4]byte
fun [100000]unsafe.Pointer // Methodensatz
}
word unsafe.Pointer // Zeiger auf den Wert
}eface entspricht emptyInterface:
type emptyInterface struct {
typ *rtype // Dynamischer konkreter Typ
word unsafe.Pointer // Zeiger auf den Wert
}Für diese beiden Typen gibt das offizielle Team klare Definitionen:
nonEmptyInterface: nonEmptyInterface is the header for an interface value with methodsemptyInterface: emptyInterface is the header for an interface{} value
Oben wurde der Begriff "dynamischer konkreter Typ" erwähnt, im Original dynamic concrete type. Go ist eine hundertprozentige statisch typisierte Sprache. "Statisch" bedeutet, dass der nach außen ersichtliche abstrakte Schnittstellentyp unverändert bleibt, während "dynamisch" bedeutet, dass der konkrete Typ, der unter der Schnittstelle gespeichert ist, variieren kann. Dies reicht aus, um die einfachen Prinzipien von Schnittstellen für das weitere Verständnis der Reflexion zu verstehen.
Brücke
Im reflect-Paket gibt es den Schnittstellentyp reflect.Type zur Darstellung von Typen in Go und den Strukturtyp reflect.Value zur Darstellung von Werten in Go:
type Type interface {
...
Name() string
PkgPath() string
Size() uintptr
String() string
Kind() Kind
...
}
type Value struct {
typ *rtype
ptr unsafe.Pointer
flag
}Der obige Code lässt viele Details aus. Es reicht zunächst zu wissen, dass diese beiden Typen existieren. Alle reflexionsbezogenen Operationen in Go basieren auf diesen beiden Typen. Das reflect-Paket stellt zwei Funktionen bereit, um Go-Typen in diese beiden Typen für Reflexionsoperationen umzuwandeln: reflect.TypeOf:
func TypeOf(i any) Typeund reflect.ValueOf:
func ValueOf(i any) ValueBeide Funktionen haben den Parametertyp any, was ein Alias für interface{} ist. Wenn Sie Reflexionsoperationen durchführen möchten, müssen Sie den Typ zunächst in interface{} umwandeln. Dies ist auch der Grund, warum oben erwähnt wurde, dass Reflexion ohne leere Schnittstellen nicht auskommt. Unstreng formuliert ist die leere Schnittstelle die Brücke zwischen dem Go-Typsystem und der Reflexion. Die folgende Abbildung veranschaulicht diesen Prozess sehr anschaulich.

TIP
Im Folgenden wird der Einfachheit halber der Alias any anstelle von interface{} verwendet.
Kern
In Go gibt es drei klassische Reflexionsgesetze, die in Verbindung mit dem oben Gesagten sehr leicht verständlich sind:
Reflexion kann eine
interface{}-Typvariable in ein Reflexionsobjekt umwandelnReflexion kann ein Reflexionsobjekt wieder in eine
interface{}-Typvariable zurückverwandelnUm ein Reflexionsobjekt zu ändern, muss sein Wert setzbar sein
Diese drei Gesetze bilden den Kern der Go-Reflexion. Wenn Sie auf typbezogene Informationen zugreifen müssen, verwenden Sie reflect.TypeOf. Wenn Sie Reflexionswerte ändern müssen, verwenden Sie reflect.ValueOf.
Typ
reflect.Type repräsentiert einen Typ in Go. Mit der Funktion reflect.TypeOf() kann eine Variable in reflect.Type umgewandelt werden. Ein Codebeispiel:
func main() {
str := "hello world!"
reflectType := reflect.TypeOf(str)
fmt.Println(reflectType)
}Ausgabe:
stringKind
Für Type verwendet Go intern reflect.Kind zur Darstellung der Basistypen in Go. Es ist im Wesentlichen ein vorzeichenloser Integer uint:
type Kind uintDas reflect-Paket verwendet Kind zur Aufzählung aller Basistypen in Go:
const (
Invalid Kind = iota
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr
Float32
Float64
Complex64
Complex128
Array
Chan
Func
Interface
Map
Pointer
Slice
String
Struct
UnsafePointer
)Der Typ Kind implementiert nur die String()-Methode des Stringer-Interfaces. Dies ist die einzige Methode dieses Typs. Der Rückgabewert der String()-Methode stammt aus einem internen Slice:
var kindNames = []string{
Invalid: "invalid",
Bool: "bool",
Int: "int",
Int8: "int8",
Int16: "int16",
Int32: "int32",
Int64: "int64",
Uint: "uint",
Uint8: "uint8",
Uint16: "uint16",
Uint32: "uint32",
Uint64: "uint64",
Uintptr: "uintptr",
Float32: "float32",
Float64: "float64",
Complex64: "complex64",
Complex128: "complex128",
Array: "array",
Chan: "chan",
Func: "func",
Interface: "interface",
Map: "map",
Pointer: "ptr",
Slice: "slice",
String: "string",
Struct: "struct",
UnsafePointer: "unsafe.Pointer",
}type Type interface{
Kind() Kind
}Mit Kind können Sie herausfinden, welcher Basistyp in einer leeren Schnittstelle gespeichert ist:
func main() {
// Eine Variable vom Typ any deklarieren
var eface any
// Wert zuweisen
eface = 100
// Mit Kind-Methode den Typ abrufen
fmt.Println(reflect.TypeOf(eface).Kind())
}Ausgabe:
intElem
type Type interface{
Elem() Type
}Mit der Methode Type.Elem() können Sie den Elementtyp einer Datenstruktur vom Typ any ermitteln. Der akzeptierte zugrunde liegende Parametertyp muss Zeiger, Slice, Array, Channel oder Map sein, andernfalls wird eine panic ausgelöst:
func main() {
var eface any
eface = map[string]int{}
rType := reflect.TypeOf(eface)
// Key() gibt den Schlüssel-Reflexionstyp der Map zurück
fmt.Println(rType.Key().Kind())
fmt.Println(rType.Elem().Kind())
}Ausgabe:
string
intZeiger können ebenfalls als Container verstanden werden. Bei Zeigern gibt Elem() den Reflexionstyp des Elements zurück, auf das der Zeiger zeigt:
func main() {
var eface any
// Zeiger zuweisen
eface = new(strings.Builder)
rType := reflect.TypeOf(eface)
// Reflexionstyp des Elements erhalten, auf das der Zeiger zeigt
vType := rType.Elem()
// Paketpfad ausgeben
fmt.Println(vType.PkgPath())
// Namen ausgeben
fmt.Println(vType.Name())
}strings
BuilderFür Arrays, Slices und Channels funktioniert es ähnlich.
Size
type Type interface{
Size() uintptr
}Mit der Size-Methode können Sie die Byte-Größe eines Typs ermitteln:
func main() {
fmt.Println(reflect.TypeOf(0).Size())
fmt.Println(reflect.TypeOf("").Size())
fmt.Println(reflect.TypeOf(complex(0, 0)).Size())
fmt.Println(reflect.TypeOf(0.1).Size())
fmt.Println(reflect.TypeOf([]string{}).Size())
}Ausgabe:
8
16
16
8
24TIP
Mit unsafe.Sizeof() erreichen Sie denselben Effekt.
Comparable
type Type interface{
Comparable() bool
}Mit der Comparable-Methode können Sie prüfen, ob ein Typ vergleichbar ist:
func main() {
fmt.Println(reflect.TypeOf("hello world!").Comparable())
fmt.Println(reflect.TypeOf(1024).Comparable())
fmt.Println(reflect.TypeOf([]int{}).Comparable())
fmt.Println(reflect.TypeOf(struct{}{}).Comparable())
}Ausgabe:
true
true
false
trueImplements
type Type interface{
Implements(u Type) bool
}Mit der Implements-Methode können Sie prüfen, ob ein Typ eine bestimmte Schnittstelle implementiert:
type MyInterface interface {
My() string
}
type MyStruct struct {
}
func (m MyStruct) My() string {
return "my"
}
type HisStruct struct {
}
func (h HisStruct) String() string {
return "his"
}
func main() {
rIface := reflect.TypeOf(new(MyInterface)).Elem()
fmt.Println(reflect.TypeOf(new(MyStruct)).Elem().Implements(rIface))
fmt.Println(reflect.TypeOf(new(HisStruct)).Elem().Implements(rIface))
}Ausgabe:
true
falseConvertibleTo
type Type interface{
ConvertibleTo(u Type) bool
}Mit der ConvertibleTo-Methode können Sie prüfen, ob ein Typ in einen anderen angegebenen Typ konvertiert werden kann:
type MyInterface interface {
My() string
}
type MyStruct struct {
}
func (m MyStruct) My() string {
return "my"
}
type HisStruct struct {
}
func (h HisStruct) String() string {
return "his"
}
func main() {
rIface := reflect.TypeOf(new(MyInterface)).Elem()
fmt.Println(reflect.TypeOf(new(MyStruct)).Elem().ConvertibleTo(rIface))
fmt.Println(reflect.TypeOf(new(HisStruct)).Elem().ConvertibleTo(rIface))
}Ausgabe:
true
falseWert
reflect.Value repräsentiert den Wert einer Reflexionsschnittstelle. Mit der Funktion reflect.ValueOf() kann eine Variable in reflect.Value umgewandelt werden:
func main() {
str := "hello world!"
reflectValue := reflect.ValueOf(str)
fmt.Println(reflectValue)
}Ausgabe:
hello world!Type
func (v Value) Type() TypeDie Type-Methode gibt den Typ eines Reflexionswerts zurück:
func main() {
num := 114514
rValue := reflect.ValueOf(num)
fmt.Println(rValue.Type())
}Ausgabe:
intElem
func (v Value) Elem() ValueErmittelt den Element-Reflexionswert eines Reflexionswerts:
func main() {
num := new(int)
*num = 114514
// Am Beispiel eines Zeigers
rValue := reflect.ValueOf(num).Elem()
fmt.Println(rValue.Interface())
}Ausgabe:
114514Zeiger
Es gibt zwei Möglichkeiten, einen Zeiger auf einen Reflexionswert zu erhalten:
// Gibt einen Zeiger-Reflexionswert zurück, der die Adresse von v repräsentiert
func (v Value) Addr() Value
// Gibt einen uintptr zurück, der auf den Rohwert von v zeigt. Entspricht uintptr(Value.Addr().UnsafePointer())
func (v Value) UnsafeAddr() uintptr
// Gibt einen uintptr zurück, der auf den Rohwert von v zeigt
// Nur wenn v's Kind Chan, Func, Map, Pointer, Slice, UnsafePointer ist, sonst panic
func (v Value) Pointer() uintptr
// Gibt einen unsafe.Pointer zurück, der auf den Rohwert von v zeigt
// Nur wenn v's Kind Chan, Func, Map, Pointer, Slice, UnsafePointer ist, sonst panic
func (v Value) UnsafePointer() unsafe.PointerBeispiel:
func main() {
num := 1024
ele := reflect.ValueOf(&num).Elem()
fmt.Println("&num", &num)
fmt.Println("Addr", ele.Addr())
fmt.Println("UnsafeAddr", unsafe.Pointer(ele.UnsafeAddr()))
fmt.Println("Pointer", unsafe.Pointer(ele.Addr().Pointer()))
fmt.Println("UnsafePointer", ele.Addr().UnsafePointer())
}Ausgabe:
&num 0xc0000a6058
Addr 0xc0000a6058
UnsafeAddr 0xc0000a6058
Pointer 0xc0000a6058
UnsafePointer 0xc0000a6058TIP
fmt.Println reflektiert automatisch den Parametertyp. Wenn es reflect.Value ist, wird automatisch Value.Interface() aufgerufen, um den Rohwert zu erhalten.
Ein weiteres Beispiel mit einer Map:
func main() {
dic := map[string]int{}
ele := reflect.ValueOf(&dic).Elem()
println(dic)
fmt.Println("Addr", ele.Addr())
fmt.Println("UnsafeAddr", *(*unsafe.Pointer)(unsafe.Pointer(ele.UnsafeAddr())))
fmt.Println("Pointer", unsafe.Pointer(ele.Pointer()))
fmt.Println("UnsafePointer", ele.UnsafePointer())
}Ausgabe:
0xc00010e4b0
Addr &map[]
UnsafeAddr 0xc00010e4b0
Pointer 0xc00010e4b0
UnsafePointer 0xc00010e4b0Wert setzen
func (v Value) Set(x Value)Wenn Sie einen Reflexionswert durch Reflexion ändern möchten, muss der Wert adressierbar sein. Sie sollten dann den Elementwert über einen Zeiger ändern, anstatt zu versuchen, den Elementwert direkt zu ändern:
func main() {
// *int
num := new(int)
*num = 114514
rValue := reflect.ValueOf(num)
// Das Element erhalten, auf das der Zeiger zeigt
ele := rValue.Elem()
fmt.Println(ele.Interface())
ele.SetInt(11)
fmt.Println(ele.Interface())
}Ausgabe:
114514
11Wert abrufen
func (v Value) Interface() (i any)Mit der Interface()-Methode können Sie den ursprünglichen Wert eines Reflexionswerts abrufen:
func main() {
var str string
str = "hello"
rValue := reflect.ValueOf(str)
if v, ok := rValue.Interface().(string); ok {
fmt.Println(v)
}
}Ausgabe:
helloFunktionen
Durch Reflexion können Sie alle Informationen einer Funktion abrufen und Funktionen reflektiv aufrufen.
Informationen
Abrufen aller Informationen einer Funktion durch den Reflexionstyp:
func Max(a, b int) int {
if a > b {
return a
}
return b
}
func main() {
rType := reflect.TypeOf(Max)
// Funktionsnamen ausgeben, Literalfunktionen haben keinen Namen
fmt.Println(rType.Name())
// Anzahl der Parameter und Rückgabewerte ausgeben
fmt.Println(rType.NumIn(), rType.NumOut())
rParamType := rType.In(0)
// Typ des ersten Parameters ausgeben
fmt.Println(rParamType.Kind())
rResType := rType.Out(0)
// Typ des ersten Rückgabewerts ausgeben
fmt.Println(rResType.Kind())
}Ausgabe:
2 1
int
intAufruf
Aufrufen einer Funktion durch den Reflexionswert:
func (v Value) Call(in []Value) []Valuefunc main() {
// Reflexionswert der Funktion abrufen
rType := reflect.ValueOf(Max)
// Parameter-Array übergeben
rResValue := rType.Call([]reflect.Value{reflect.ValueOf(18), reflect.ValueOf(50)})
for _, value := range rResValue {
fmt.Println(value.Interface())
}
}Ausgabe:
50Strukturen
Angenommen, es gibt folgende Struktur:
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Address string `json:"address"`
money int
}
func (p Person) Talk(msg string) string {
return msg
}Felder zugreifen
Die Struktur reflect.StructField sieht wie folgt aus:
type StructField struct {
// Feldname
Name string
// Paketname
PkgPath string
// Typname
Type Type
// Tag
Tag StructTag
// Byte-Offset des Feldes
Offset uintptr
// Index
Index []int
// Ob es ein eingebettetes Feld ist
Anonymous bool
}Es gibt zwei Methoden, um auf Strukturfelder zuzugreifen: über den Index oder über den Namen.
type Type interface{
Field(i int) StructField
}Beispiel für den Zugriff über den Index:
func main() {
rType := reflect.TypeOf(new(Person)).Elem()
// Anzahl der Strukturfelder ausgeben
fmt.Println(rType.NumField())
for i := 0; i < rType.NumField(); i++ {
structField := rType.Field(i)
fmt.Println(structField.Index, structField.Name, structField.Type, structField.Offset, structField.IsExported())
}
}Ausgabe:
4
[0] Name string 0 true
[1] Age int 16 true
[2] Address string 24 true
[3] money int 40 falsetype Type interface{
FieldByName(name string) (StructField, bool)
}Beispiel für den Zugriff über den Namen:
func main() {
rType := reflect.TypeOf(new(Person)).Elem()
// Anzahl der Strukturfelder ausgeben
fmt.Println(rType.NumField())
if field, ok := rType.FieldByName("money"); ok {
fmt.Println(field.Name, field.Type, field.IsExported())
}
}Ausgabe:
4
money int falseFelder ändern
Um einen Strukturfeldwert zu ändern, müssen Sie einen Strukturzeiger übergeben. Hier ist ein Beispiel:
func main() {
// Zeiger übergeben
rValue := reflect.ValueOf(&Person{
Name: "",
Age: 0,
Address: "",
money: 0,
}).Elem()
// Feld abrufen
name := rValue.FieldByName("Name")
// Feldwert ändern
if (name != reflect.Value{}) { // Wenn reflect.Value{} zurückgegeben wird, existiert das Feld nicht
name.SetString("jack")
}
// Struktur ausgeben
fmt.Println(rValue.Interface())
}Ausgabe:
{jack 0 0}Für private Felder einer Struktur sind zusätzliche Schritte erforderlich:
func main() {
// Zeiger übergeben
rValue := reflect.ValueOf(&Person{
Name: "",
Age: 0,
Address: "",
money: 0,
}).Elem()
// Ein privates Feld abrufen
money := rValue.FieldByName("money")
// Feldwert ändern
if (money != reflect.Value{}) {
// Zeiger-Reflexionswert auf das nicht exportierte Feld der Struktur konstruieren
p := reflect.NewAt(money.Type(), money.Addr().UnsafePointer())
// Das Element erhalten, auf das der Zeiger zeigt, also das zu ändernde Feld
field := p.Elem()
// Wert ändern
field.SetInt(164)
}
// Struktur ausgeben
fmt.Printf("%+v\n", rValue.Interface())
}Tag zugreifen
Nachdem Sie StructField erhalten haben, können Sie direkt auf dessen Tag zugreifen:
// Wenn nicht vorhanden, ist ok false
func (tag StructTag) Lookup(key string) (value string, ok bool)
// Wenn nicht vorhanden, wird ein leerer String zurückgegeben
func (tag StructTag) Get(key string) stringBeispiel:
func main() {
rType := reflect.TypeOf(new(Person)).Elem()
name, ok := rType.FieldByName("Name")
if ok {
fmt.Println(name.Tag.Lookup("json"))
fmt.Println(name.Tag.Get("json"))
}
}Ausgabe:
name true
nameMethoden zugreifen
Der Prozess des Zugriffs auf Methoden ähnelt dem Zugriff auf Felder, nur die Funktionssignaturen unterscheiden sich leicht. Die Struktur reflect.Method:
type Method struct {
// Methodenname
Name string
// Paketname
PkgPath string
// Methodentyp
Type Type
// Der Methode entsprechende Funktion, der erste Parameter ist der Empfänger
Func Value
// Index
Index int
}Beispiel für den Zugriff auf Methodeninformationen:
func main() {
// Reflexionstyp der Struktur abrufen
rType := reflect.TypeOf(new(Person)).Elem()
// Anzahl der Methoden ausgeben
fmt.Println(rType.NumMethod())
// Methodeninformationen durchlaufen und ausgeben
for i := 0; i < rType.NumMethod(); i++ {
method := rType.Method(i)
fmt.Println(method.Index, method.Name, method.Type, method.IsExported())
}
}Ausgabe:
1
0 Talk func(main.Person, string) string trueUm Details zu Parametern und Rückgabewerten der Methode zu erhalten, können Sie Method.Func verwenden. Der Prozess entspricht dem Zugriff auf Funktionsinformationen:
func main() {
// Reflexionstyp der Struktur abrufen
rType := reflect.TypeOf(new(Person)).Elem()
// Anzahl der Methoden ausgeben
fmt.Println(rType.NumMethod())
// Methodeninformationen durchlaufen und ausgeben
for i := 0; i < rType.NumMethod(); i++ {
method := rType.Method(i)
fmt.Println(method.Index, method.Name, method.Type, method.IsExported())
fmt.Println("Methodenparameter")
for i := 0; i < method.Func.Type().NumIn(); i++ {
fmt.Println(method.Func.Type().In(i).String())
}
fmt.Println("Methodenrückgabewerte")
for i := 0; i < method.Func.Type().NumOut(); i++ {
fmt.Println(method.Func.Type().Out(i).String())
}
}
}Man sieht, dass der erste Parameter main.Person ist, also der Empfängertyp:
1
0 Talk func(main.Person, string) string true
Methodenparameter
main.Person
string
Methodenrückgabewerte
stringMethoden aufrufen
Das Aufrufen von Methoden ähnelt dem Aufrufen von Funktionen, und Sie müssen den Empfänger nicht manuell übergeben:
func main() {
// Reflexionstyp der Struktur abrufen
rValue := reflect.ValueOf(new(Person)).Elem()
// Anzahl der Methoden ausgeben
fmt.Println(rValue.NumMethod())
// Methodeninformationen durchlaufen und ausgeben
talk := rValue.MethodByName("Talk")
if (talk != reflect.Value{}) {
// Methode aufrufen und Rückgabewerte erhalten
res := talk.Call([]reflect.Value{reflect.ValueOf("hello,reflect!")})
// Rückgabewerte durchlaufen und ausgeben
for _, re := range res {
fmt.Println(re.Interface())
}
}
}Ausgabe:
1
hello,reflect!Erstellen
Durch Reflexion können neue Werte konstruiert werden. Das reflect-Paket stellt für einige spezielle Typen verschiedene, bequemere Funktionen bereit.
Basistypen
// Gibt einen Zeiger-Reflexionswert zurück, der auf den Reflexionswert zeigt
func New(typ Type) ValueBeispiel mit string:
func main() {
rValue := reflect.New(reflect.TypeOf(*new(string)))
rValue.Elem().SetString("hello world!")
fmt.Println(rValue.Elem().Interface())
}hello world!Strukturen
Die Erstellung von Strukturen verwendet ebenfalls die Funktion reflect.New:
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Address string `json:"address"`
money int
}
func (p Person) Talk(msg string) string {
return msg
}
func main() {
// Struktur-Reflexionswert erstellen
rType := reflect.TypeOf(new(Person)).Elem()
person := reflect.New(rType).Elem()
fmt.Println(person.Interface())
}Ausgabe:
{ 0 0}Slices
Reflexive Erstellung eines Slices:
func MakeSlice(typ Type, len, cap int) Valuefunc main() {
// Slice-Reflexionswert erstellen
rValue := reflect.MakeSlice(reflect.TypeOf(*new([]int)), 10, 10)
// Durchlaufen und Werte zuweisen
for i := 0; i < 10; i++ {
rValue.Index(i).SetInt(int64(i))
}
fmt.Println(rValue.Interface())
}[0 1 2 3 4 5 6 7 8 9]Map
Reflexive Erstellung einer Map:
func MakeMapWithSize(typ Type, n int) Valuefunc main() {
// Map-Reflexionswert erstellen
rValue := reflect.MakeMapWithSize(reflect.TypeOf(*new(map[string]int)), 10)
// Wert setzen
rValue.SetMapIndex(reflect.ValueOf("a"), reflect.ValueOf(1))
fmt.Println(rValue.Interface())
}map[a:1]Channel
Reflexive Erstellung eines Channels:
func MakeChan(typ Type, buffer int) Valuefunc main() {
// Channel-Reflexionswert erstellen
makeChan := reflect.MakeChan(reflect.TypeOf(new(chan int)).Elem(), 0)
fmt.Println(makeChan.Interface())
}Funktionen
Reflexive Erstellung einer Funktion:
func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Valuefunc main() {
// Wrapping-Typ und Funktionskörper übergeben
fn := reflect.MakeFunc(reflect.TypeOf(new(func(int))).Elem(), func(args []reflect.Value) (results []reflect.Value) {
for _, arg := range args {
fmt.Println(arg.Interface())
}
return nil
})
fmt.Println(fn.Type())
fn.Call([]reflect.Value{reflect.ValueOf(1024)})
}Ausgabe:
func(int)
1024Vollständige Gleichheit
reflect.DeepEqual ist eine Funktion aus dem Reflexionspaket, die prüft, ob zwei Variablen vollständig gleich sind. Die Signatur lautet:
func DeepEqual(x, y any) boolDiese Funktion behandelt jeden Basistyp. Im Folgenden sind einige Typprüfmethoden aufgeführt:
- Arrays: Jedes Element im Array ist vollständig gleich
- Slices: Wenn beide
nilsind, gelten sie als vollständig gleich, oder wenn beide nicht leer sind, sind alle Elemente im Längenbereich vollständig gleich - Strukturen: Alle Felder sind vollständig gleich
- Maps: Wenn beide
nilsind, sind sie vollständig gleich; wenn beide nichtnilsind, ist jeder Wert, auf den jeder Schlüssel abbildet, vollständig gleich - Zeiger: Sie zeigen auf dasselbe Element oder die Elemente, auf die sie zeigen, sind vollständig gleich
- Schnittstellen: Die konkreten Typen der Schnittstellen sind vollständig gleich
- Funktionen: Nur wenn beide
nilsind, sind sie vollständig gleich, sonst nicht
Beispiele:
Slices
func main() {
a := make([]int, 100)
b := make([]int, 100)
fmt.Println(reflect.DeepEqual(a, b))
}Ausgabe:
trueStrukturen
func main() {
mike := Person{
Name: "mike",
Age: 39,
Father: nil,
}
jack := Person{
Name: "jack",
Age: 18,
Father: &mike,
}
tom := Person{
Name: "tom",
Age: 18,
Father: &mike,
}
fmt.Println(reflect.DeepEqual(mike, jack))
fmt.Println(reflect.DeepEqual(tom, jack))
fmt.Println(reflect.DeepEqual(jack, jack))
}Ausgabe:
false
false
true