Skip to content

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:

go
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:

go
type eface struct {
   _type *_type // Typ
   data  unsafe.Pointer // Zeiger auf den Wert
}

Beide Strukturen haben entsprechende Strukturtypen im reflect-Paket. iface entspricht nonEmptyInterface:

go
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:

go
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 methods
  • emptyInterface: 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:

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:

go
func TypeOf(i any) Type

und reflect.ValueOf:

go
func ValueOf(i any) Value

Beide 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:

  1. Reflexion kann eine interface{}-Typvariable in ein Reflexionsobjekt umwandeln

  2. Reflexion kann ein Reflexionsobjekt wieder in eine interface{}-Typvariable zurückverwandeln

  3. Um 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:

go
func main() {
  str := "hello world!"
  reflectType := reflect.TypeOf(str)
  fmt.Println(reflectType)
}

Ausgabe:

string

Kind

Für Type verwendet Go intern reflect.Kind zur Darstellung der Basistypen in Go. Es ist im Wesentlichen ein vorzeichenloser Integer uint:

go
type Kind uint

Das reflect-Paket verwendet Kind zur Aufzählung aller Basistypen in Go:

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:

go
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",
}
go
type Type interface{
    Kind() Kind
}

Mit Kind können Sie herausfinden, welcher Basistyp in einer leeren Schnittstelle gespeichert ist:

go
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:

int

Elem

go
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:

go
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
int

Zeiger können ebenfalls als Container verstanden werden. Bei Zeigern gibt Elem() den Reflexionstyp des Elements zurück, auf das der Zeiger zeigt:

go
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
Builder

Für Arrays, Slices und Channels funktioniert es ähnlich.

Size

go
type Type interface{
    Size() uintptr
}

Mit der Size-Methode können Sie die Byte-Größe eines Typs ermitteln:

go
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
24

TIP

Mit unsafe.Sizeof() erreichen Sie denselben Effekt.

Comparable

go
type Type interface{
    Comparable() bool
}

Mit der Comparable-Methode können Sie prüfen, ob ein Typ vergleichbar ist:

go
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
true

Implements

go
type Type interface{
    Implements(u Type) bool
}

Mit der Implements-Methode können Sie prüfen, ob ein Typ eine bestimmte Schnittstelle implementiert:

go
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
false

ConvertibleTo

go
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:

go
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
false

Wert

reflect.Value repräsentiert den Wert einer Reflexionsschnittstelle. Mit der Funktion reflect.ValueOf() kann eine Variable in reflect.Value umgewandelt werden:

go
func main() {
  str := "hello world!"
  reflectValue := reflect.ValueOf(str)
  fmt.Println(reflectValue)
}

Ausgabe:

hello world!

Type

go
func (v Value) Type() Type

Die Type-Methode gibt den Typ eines Reflexionswerts zurück:

go
func main() {
   num := 114514
   rValue := reflect.ValueOf(num)
   fmt.Println(rValue.Type())
}

Ausgabe:

int

Elem

go
func (v Value) Elem() Value

Ermittelt den Element-Reflexionswert eines Reflexionswerts:

go
func main() {
   num := new(int)
   *num = 114514
   // Am Beispiel eines Zeigers
   rValue := reflect.ValueOf(num).Elem()
   fmt.Println(rValue.Interface())
}

Ausgabe:

114514

Zeiger

Es gibt zwei Möglichkeiten, einen Zeiger auf einen Reflexionswert zu erhalten:

go
// 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.Pointer

Beispiel:

go
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 0xc0000a6058

TIP

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:

go
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 0xc00010e4b0

Wert setzen

go
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:

go
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
11

Wert abrufen

go
func (v Value) Interface() (i any)

Mit der Interface()-Methode können Sie den ursprünglichen Wert eines Reflexionswerts abrufen:

go
func main() {
   var str string
   str = "hello"
   rValue := reflect.ValueOf(str)
   if v, ok := rValue.Interface().(string); ok {
      fmt.Println(v)
   }
}

Ausgabe:

hello

Funktionen

Durch Reflexion können Sie alle Informationen einer Funktion abrufen und Funktionen reflektiv aufrufen.

Informationen

Abrufen aller Informationen einer Funktion durch den Reflexionstyp:

go
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
int

Aufruf

Aufrufen einer Funktion durch den Reflexionswert:

go
func (v Value) Call(in []Value) []Value
go
func 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:

50

Strukturen

Angenommen, es gibt folgende Struktur:

go
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:

go
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.

go
type Type interface{
    Field(i int) StructField
}

Beispiel für den Zugriff über den Index:

go
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 false
go
type Type interface{
    FieldByName(name string) (StructField, bool)
}

Beispiel für den Zugriff über den Namen:

go
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 false

Felder ändern

Um einen Strukturfeldwert zu ändern, müssen Sie einen Strukturzeiger übergeben. Hier ist ein Beispiel:

go
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:

go
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:

go
// 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) string

Beispiel:

go
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
name

Methoden zugreifen

Der Prozess des Zugriffs auf Methoden ähnelt dem Zugriff auf Felder, nur die Funktionssignaturen unterscheiden sich leicht. Die Struktur reflect.Method:

go
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:

go
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 true

Um Details zu Parametern und Rückgabewerten der Methode zu erhalten, können Sie Method.Func verwenden. Der Prozess entspricht dem Zugriff auf Funktionsinformationen:

go
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
string

Methoden aufrufen

Das Aufrufen von Methoden ähnelt dem Aufrufen von Funktionen, und Sie müssen den Empfänger nicht manuell übergeben:

go
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

go
// Gibt einen Zeiger-Reflexionswert zurück, der auf den Reflexionswert zeigt
func New(typ Type) Value

Beispiel mit string:

go
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:

go
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:

go
func MakeSlice(typ Type, len, cap int) Value
go
func 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:

go
func MakeMapWithSize(typ Type, n int) Value
go
func 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:

go
func MakeChan(typ Type, buffer int) Value
go
func main() {
   // Channel-Reflexionswert erstellen
   makeChan := reflect.MakeChan(reflect.TypeOf(new(chan int)).Elem(), 0)
   fmt.Println(makeChan.Interface())
}

Funktionen

Reflexive Erstellung einer Funktion:

go
func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value
go
func 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)
1024

Vollständige Gleichheit

reflect.DeepEqual ist eine Funktion aus dem Reflexionspaket, die prüft, ob zwei Variablen vollständig gleich sind. Die Signatur lautet:

go
func DeepEqual(x, y any) bool

Diese Funktion behandelt jeden Basistyp. Im Folgenden sind einige Typprüfmethoden aufgeführt:

  • Arrays: Jedes Element im Array ist vollständig gleich
  • Slices: Wenn beide nil sind, 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 nil sind, sind sie vollständig gleich; wenn beide nicht nil sind, 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 nil sind, sind sie vollständig gleich, sonst nicht

Beispiele:

Slices

go
func main() {
   a := make([]int, 100)
   b := make([]int, 100)
   fmt.Println(reflect.DeepEqual(a, b))
}

Ausgabe:

true

Strukturen

go
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

Golang by www.golangdev.cn edit