Skip to content

Yansıma

Yansıma, çalışma zamanında dilin kendi yapısını kontrol etmeye yarayan bir mekanizmadır. Bazı sorunlara esnek bir şekilde yanıt vermemizi sağlar. Ancak beraberinde getirdiği dezavantajlar da açıktır. Örneğin performans sorunları vb. Go'da, yansıma interface{} ile yakından ilişkilidir. Büyük ölçüde, interface{}'nin göründüğü her yerde yansıma vardır. Go'daki yansıma API'leri standart kütüphane reflect paketi tarafından sağlanır.

Arayüz

Başlamadan önce runtime paketindeki iki arayüzü kısaca anlayalım. Go'da, arayüzler yapı taşlarıdır. Go çalışma zamanında arayüzleri iki büyük kategoriye ayırır. Birincisi metot kümesi olmayan arayüzler, diğeri metot kümesi olan arayüzlerdir. Metot kümesi içeren arayüzler için, çalışma zamanında aşağıdaki yapı iface tarafından temsil edilir

go
type iface struct {
   tab  *itab // Veri türü, arayüz türü, metot kümesi vb. içerir
   data unsafe.Pointer // Değere işaret eden işaretçi
}

Metot kümesi olmayan arayüzler için, çalışma zamanında eface yapısı tarafından temsil edilir. Aşağıdaki gibi

go
type eface struct {
   _type *_type // Tür
   data  unsafe.Pointer // Değere işaret eden işaretçi
}

Bu iki yapı reflect paketi altında onlara karşılık gelen yapı türlerine sahiptir. iface, nonEmptyInterface'e karşılık gelir

go
type nonEmptyInterface struct {
  itab *struct {
    ityp *rtype // Statik arayüz türü
    typ  *rtype // Dinamik somut tür
    hash uint32 // Tür hash
    _    [4]byte
    fun  [100000]unsafe.Pointer // Metot kümesi
  }
  word unsafe.Pointer // Değere işaret eden işaretçi
}

eface ise emptyInterface'e karşılık gelir

go
type emptyInterface struct {
   typ  *rtype // Dinamik somut tür
   word unsafe.Pointer // İşaretçiye sahip değer
}

Bu iki tür için, resmi çok net bir tanım vermiştir:

  • nonEmptyInterface: nonEmptyInterface is the header for an interface value with methods
  • emptyInterface: emptyInterface is the header for an interface{} value

Yukarıda dinamik somut tür terimi anıldı. Orijinal metin dynamic concrete type şeklindedir. İlk olarak Go dili yüzde yüz statik tür dilidir. Statik kelime dışa dönük soyut arayüz türünün değişmez olduğunu gösterir. Dinamik ise altta saklanan somut uygulamanın türünün değişebileceğini gösterir. Bu noktaya kadar, arayüzün basit prensibi için yansıma öğrenimini karşılamak için bu kadarı yeterlidir.

Köprü

reflect paketi altında, Go'daki türleri temsil etmek için reflect.Type arayüz türü ve Go'daki değerleri temsil etmek için reflect.Value yapı türü bulunur

go
type Type interface {
    ...

    Name() string

  PkgPath() string

  Size() uintptr

  String() string

  Kind() Kind

    ...
}

type Value struct {

   typ *rtype

   ptr unsafe.Pointer

   flag

}

Yukarıdaki kod birçok detayı atlar. Şimdilik sadece bu iki türün varlığını anlayın. Go'daki tüm yansıma ile ilgili işlemler bu iki türe dayanır. reflect paketi, Go'daki türleri yukarıdaki iki türe dönüştürmek için iki fonksiyon sağlar. Böylece yansıma işlemleri yapılabilir. Bunlar sırasıyla reflect.TypeOf fonksiyonu

go
func TypeOf(i any) Type

ve reflect.ValueOf fonksiyonu

go
func ValueOf(i any) Value

Her iki fonksiyonun parametre türünün any yani interface{}'nin takma adı olduğu görülebilir. Yansıma işlemleri yapmak istiyorsanız, önce türünüzü interface{}'ye dönüştürmeniz gerekir. Bu da neden前面'de yansıma varsa boş arayüzün olduğu söylenmesinin nedenidir. Tam olarak söylenemese de, boş arayüz Go tür sistemi ile yansıma arasındaki köprüdür. Aşağıdaki resim bu süreci canlı bir şekilde betimler.

::: ipucu

Aşağıdaki metinde kolaylık sağlamak için, interface{} yerine takma ad any kullanılacaktır.

:::

Çekirdek

Go'da üç klasik yansıma yasası vardır. Yukarıda anlatılanlarla birleştirildiğinde anlaşılması çok kolaydır. Sırasıyla aşağıdaki gibidir:

  1. Yansıma, interface{} türü değişkeni yansıma nesnesine dönüştürebilir

  2. Yansıma, yansıma nesnesini interface{} türü değişkene geri döndürebilir

  3. Yansıma nesnesini değiştirmek için, değerin ayarlanabilir olması gerekir

Bu üç yasa Go yansımasının çekirdeğidir. Tür ile ilgili bilgilere erişmek gerektiğinde, reflect.TypeOf kullanılır. Yansıma değerini değiştirmek gerektiğinde, reflect.ValueOf kullanılır.

Tür

reflect.Type, Go'daki türü temsil eder. reflect.TypeOf() fonksiyonu kullanılarak değişken reflect.Type'a dönüştürülebilir. Kod örneği aşağıdaki gibidir

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

Çıktı sonucu

string

Kind

Type için, Go içsel olarak Go'daki temel türleri temsil etmek için reflect.Kind kullanır. Temelde işaretsiz tamsayı uint'tür.

go
type Kind uint

reflect paketi Kind kullanarak Go'daki tüm temel türleri numaralandırır. Aşağıda gösterildiği gibi

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
)

Kind türü sadece Stringer arayüzünün String() metodunu uygular. Bu türün sadece bu tek metodu vardır. String() metodunun dönüş değeri içsel bir slice'dan gelir. Aşağıda gösterildiği gibi. Bu yazım ilk bakışta map gibi görünür. Ancak aslında bu Go'daki özel bir yazımdır: indeks ifadeleri slice literal'lerinde (index expressions in slice literals)

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
}

Kind aracılığıyla, boş arayüzün sakladığı değerin hangi temel tür olduğunu bilebiliriz. Örneğin

go
func main() {
    // any türünde bir değişken bildir
  var eface any
    // Değer ata
  eface = 100
    // Kind metodu aracılığıyla türünü al
  fmt.Println(reflect.TypeOf(eface).Kind())
}

Çıktı sonucu

int

Elem

go
type Type interface{
    Elem() Type
}

Type.Elem() metodu kullanılarak, any türündeki veri yapısının sakladığı element türü belirlenebilir. Kabul edilebilen alt parametre türlerinden biri işaretçi, slice, dizi, kanal, harita olmalıdır. Aksi takdirde panic oluşur. Aşağıda kod örneği bulunmaktadır

go
func main() {
  var eface any
  eface = map[string]int{}
  rType := reflect.TypeOf(eface)
    // key() map'in anahtar yansıma türünü döndürür
  fmt.Println(rType.Key().Kind())
  fmt.Println(rType.Elem().Kind())
}

Çıktı

string
int

İşaretçi de bir kapsayıcı olarak anlaşılabilir. İşaretçi için Elem() kullanıldığında, işaret ettiği elementin yansıma türü alınır. Kod örneği aşağıdaki gibidir

go
func main() {
  var eface any
    // İşaretçi ata
  eface = new(strings.Builder)
  rType := reflect.TypeOf(eface)
    // İşaretçinin işaret ettiği elementin yansıma türünü al
  vType := rType.Elem()
    // Paket yolunu yazdır
  fmt.Println(vType.PkgPath())
    // Adını yazdır
  fmt.Println(vType.Name())
}
strings
Builder

Dizi, slice, kanal için kullanım benzerdir.

Size

go
type Type interface{
    Size() uintptr
}

Size metodu aracılığıyla ilgili türün kapladığı byte boyutu alınabilir. Örnek aşağıdaki gibidir

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

Çıktı sonucu

8
16
16
8
24

::: ipucu

unsafe.Sizeof() kullanarak aynı etkiyi elde edebilirsiniz.

:::

Comparable

go
type Type interface{
    Comparable() bool
}

Comparable metodu aracılığıyla bir türün karşılaştırılabilir olup olmadığı belirlenebilir. Örnek aşağıdaki gibidir

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

Çıktı aşağıdaki gibidir

true
true
false
true

Implements

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

Implements metodu aracılığıyla bir türün belirli bir arayüzü uygulayıp uygulamadığı belirlenebilir

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

Çıktı sonucu

true
false

ConvertibleTo

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

ConvertibleTo metodu kullanılarak bir türün başka bir belirtilen türe dönüştürülüp dönüştürülemeyeceği belirlenebilir

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

Çıktı

true
false

Değer

reflect.Value, yansıma arayüzünün değerini temsil eder. reflect.ValueOf() fonksiyonu kullanılarak değişken reflect.Value'a dönüştürülebilir. Kod örneği aşağıdaki gibidir

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

Çıktı sonucu

hello world!

Type

go
func (v Value) Type() Type

Type metodu bir yansıma değerinin türünü alabilir

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

Çıktı

int

Elem

go
func (v Value) Elem() Value

Bir yansıma değerinin element yansıma değerini alır

go
func main() {
   num := new(int)
   *num = 114514
   // İşaretçi örneği
   rValue := reflect.ValueOf(num).Elem()
   fmt.Println(rValue.Interface())
}

Çıktı

114514

İşaretçi

Bir yansıma değerinin işaretçisini almanın iki yolu vardır

go
// v'nin adresini temsil eden bir işaretçi yansıma değeri döndürür
func (v Value) Addr() Value

// v'nin orijinal değerine işaret eden bir uintptr döndürür. uintptr(Value.Addr().UnsafePointer()) ile eşdeğerdir
func (v Value) UnsafeAddr() uintptr

// v'nin orijinal değerine işaret eden bir uintptr döndürür
// Sadece v'nin Kind'ı Chan, Func, Map, Pointer, Slice, UnsafePointer olduğunda geçerlidir, aksi takdirde panic oluşur
func (v Value) Pointer() uintptr

// v'nin orijinal değerine işaret eden bir unsafe.Pointer döndürür
// Sadece v'nin Kind'ı Chan, Func, Map, Pointer, Slice, UnsafePointer olduğunda geçerlidir, aksi takdirde panic oluşur
func (v Value) UnsafePointer() unsafe.Pointer

Örnek aşağıdaki gibidir

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

Çıktı

&num 0xc0000a6058
Addr 0xc0000a6058
UnsafeAddr 0xc0000a6058
Pointer 0xc0000a6058
UnsafePointer 0xc0000a6058

::: ipucu

fmt.Println parametre türünü yansıma ile alır. Eğer reflect.Value türü ise, otomatik olarak Value.Interface() çağırarak orijinal değerini alır.

:::

Bir map ile tekrar deneyelim

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

Çıktı

0xc00010e4b0
Addr &map[]
UnsafeAddr 0xc00010e4b0
Pointer 0xc00010e4b0
UnsafePointer 0xc00010e4b0

Değer Ayarlama

go
func (v Value) Set(x Value)

Yansıma yoluyla yansıma değerini değiştirmek istiyorsanız, değeri alınabilir olmalıdır. Bu durumda element değerini doğrudan değiştirmeye çalışmak yerine, işaretçi aracılığıyla element değerini değiştirmelisiniz.

go
func main() {
   // *int
   num := new(int)
   *num = 114514
   rValue := reflect.ValueOf(num)
    // İşaretçinin işaret ettiği elementi al
   ele := rValue.Elem()
   fmt.Println(ele.Interface())
   ele.SetInt(11)
   fmt.Println(ele.Interface())
}

Çıktı aşağıdaki gibidir

114514
11

Değer Alma

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

Interface() metodu aracılığıyla yansıma değerinin orijinal değeri alınabilir

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

Çıktı

hello

Fonksiyon

Yansıma aracılığıyla fonksiyonun tüm bilgileri alınabilir ve fonksiyon yansıma ile çağrılabilir

Bilgi

Yansıma türü aracılığıyla fonksiyonun tüm bilgilerini alın

go
func Max(a, b int) int {
   if a > b {
      return a
   }
   return b
}

func main() {
   rType := reflect.TypeOf(Max)
   // Fonksiyon adını yazdır, literal fonksiyon türünün adı yoktur
   fmt.Println(rType.Name())
   // Parametre, dönüş değeri sayısını yazdır
   fmt.Println(rType.NumIn(), rType.NumOut())
   rParamType := rType.In(0)
   // İlk parametrenin türünü yazdır
   fmt.Println(rParamType.Kind())
   rResType := rType.Out(0)
   // İlk dönüş değerinin türünü yazdır
   fmt.Println(rResType.Kind())
}

Çıktı


2 1
int
int

Çağrı

Yansıma değeri aracılığıyla fonksiyonu çağır

go
func (v Value) Call(in []Value) []Value
go
func main() {
   // Fonksiyonun yansıma değerini al
   rType := reflect.ValueOf(Max)
   // Parametre dizisini gir
   rResValue := rType.Call([]reflect.Value{reflect.ValueOf(18), reflect.ValueOf(50)})
   for _, value := range rResValue {
      fmt.Println(value.Interface())
   }
}

Çıktı

50

Yapı

Aşağıdaki yapı varsayılsın

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
}

Alanlara Erişim

reflect.StructField yapısının yapısı aşağıdaki gibidir

go
type StructField struct {
  // Alan adı
  Name string
  // Paket adı
  PkgPath string
  // Tür adı
  Type      Type
  // Tag
  Tag       StructTag
  // Alanın byte ofseti
  Offset    uintptr
  // İndeks
  Index     []int
  // İç içe alan olup olmadığı
  Anonymous bool
}

Yapı alanına erişmenin iki yolu vardır. Biri indeks aracılığıyla erişim, diğeri ad aracılığıyla erişim.

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

İndeks aracılığıyla erişim örneği aşağıdaki gibidir

go
func main() {
  rType := reflect.TypeOf(new(Person)).Elem()
  // Yapı alanlarının sayısını yazdır
  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())
  }
}

Çıktı

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

Ad aracılığıyla erişim örneği aşağıdaki gibidir

go
func main() {
   rType := reflect.TypeOf(new(Person)).Elem()
   // Yapı alanlarının sayısını yazdır
   fmt.Println(rType.NumField())
   if field, ok := rType.FieldByName("money"); ok {
      fmt.Println(field.Name, field.Type, field.IsExported())
   }
}

Çıktı

4
money int false

Alanı Değiştir

Yapı alan değerini değiştirmek istiyorsanız, bir yapı işaretçisi girmelisiniz. Aşağıda bir alanı değiştiren örnek bulunmaktadır

go
func main() {
  // İşaretçi gir
  rValue := reflect.ValueOf(&Person{
    Name:    "",
    Age:     0,
    Address: "",
    money:   0,
  }).Elem()

  // Alanı al
  name := rValue.FieldByName("Name")
  // Alan değerini değiştir
  if (name != reflect.Value{}) { // Eğer reflect.Value{} döndürürse, bu alanın mevcut olmadığı anlamına gelir
    name.SetString("jack")
  }
  // Yapıyı yazdır
  fmt.Println(rValue.Interface())
}

Çıktı

{jack 0  0}

Yapı özel alanını değiştirmek için, bazı ek işlemler yapılmalıdır. Aşağıdaki gibi

go
func main() {
  // İşaretçi gir
  rValue := reflect.ValueOf(&Person{
    Name:    "",
    Age:     0,
    Address: "",
    money:   0,
  }).Elem()

  // Özel bir alan al
  money := rValue.FieldByName("money")
  // Alan değerini değiştir
  if (money != reflect.Value{}) {
    // Bu yapıya ait dışa aktarılmamış alana işaret eden işaretçi yansıma değerini oluştur
    p := reflect.NewAt(money.Type(), money.Addr().UnsafePointer())
    // Bu işaretçinin işaret ettiği elementi al, yani değiştirilecek alan
    field := p.Elem()
    // Değeri değiştir
    field.SetInt(164)
  }
  // Yapıyı yazdır
  fmt.Printf("%+v\n", rValue.Interface())
}

Tag'e Erişim

StructField alındıktan sonra, Tag'ine doğrudan erişilebilir

go
// Eğer mevcut değilse, ok false'dur
func (tag StructTag) Lookup(key string) (value string, ok bool)

// Eğer mevcut değilse, boş string döndürür
func (tag StructTag) Get(key string) string

Örnek aşağıdaki gibidir

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

Çıktı

name true
name

Metotlara Erişim

Metotlara erişim alanlara erişim sürecine benzer. Sadece fonksiyon imzası biraz farklıdır. reflect.Method yapısı aşağıdaki gibidir

go
type Method struct {
  // Metot adı
  Name string
  // Paket adı
  PkgPath string
  // Metot türü
  Type  Type
  // Metota karşılık gelen fonksiyon, ilk parametre alıcıdır
  Func  Value
  // İndeks
  Index int
}

Metot bilgilerine erişim örneği aşağıdaki gibidir

go
func main() {
  // Yapı yansıma türünü al
  rType := reflect.TypeOf(new(Person)).Elem()
  // Metot sayısını yazdır
  fmt.Println(rType.NumMethod())
  // Metot bilgilerini yineleyerek yazdır
  for i := 0; i < rType.NumMethod(); i++ {
    method := rType.Method(i)
    fmt.Println(method.Index, method.Name, method.Type, method.IsExported())
  }
}

Çıktı

1
0 Talk func(main.Person, string) string true

Metot parametreleri ve dönüş değerleri detaylarını almak istiyorsanız, Method.Func aracılığıyla alabilirsiniz. Süreç fonksiyon bilgilerine erişim ile aynıdır. Yukarıdaki kodu biraz değiştirin

go
func main() {
  // Yapı yansıma türünü al
  rType := reflect.TypeOf(new(Person)).Elem()
  // Metot sayısını yazdır
  fmt.Println(rType.NumMethod())
  // Metot bilgilerini yineleyerek yazdır
  for i := 0; i < rType.NumMethod(); i++ {
    method := rType.Method(i)
    fmt.Println(method.Index, method.Name, method.Type, method.IsExported())
    fmt.Println("Metot parametreleri")
    for i := 0; i < method.Func.Type().NumIn(); i++ {
      fmt.Println(method.Func.Type().In(i).String())
    }
    fmt.Println("Metot dönüş değerleri")
    for i := 0; i < method.Func.Type().NumOut(); i++ {
      fmt.Println(method.Func.Type().Out(i).String())
    }
  }
}

İlk parametrenin main.Person olduğunu görebilirsiniz. Yani alıcı türü

1
0 Talk func(main.Person, string) string true
Metot parametreleri
main.Person
string
Metot dönüş değerleri
string

Metot Çağrısı

Metot çağrısı fonksiyon çağrısı sürecine benzer. Ve alıcıyı manuel olarak girmeniz gerekmez. Örnek aşağıdaki gibidir

go
func main() {
   // Yapı yansıma türünü al
   rValue := reflect.ValueOf(new(Person)).Elem()
   // Metot sayısını yazdır
   fmt.Println(rValue.NumMethod())
   // Metot bilgilerini yineleyerek yazdır
   talk := rValue.MethodByName("Talk")
   if (talk != reflect.Value{}) {
      // Metodu çağır ve dönüş değerini al
      res := talk.Call([]reflect.Value{reflect.ValueOf("hello,reflect!")})
      // Dönüş değerlerini yineleyerek yazdır
      for _, re := range res {
         fmt.Println(re.Interface())
      }
   }
}

Çıktı

1
hello,reflect!

Oluşturma

Yansıma aracılığıyla yeni değerler oluşturulabilir. reflect paketi aynı zamanda bazı özel türler için daha uygun fonksiyonlar sağlar.

Temel Türler

go
// Yansıma değerine işaret eden işaretçi yansıma değerini döndürür
func New(typ Type) Value

string örneği ile

go
func main() {
   rValue := reflect.New(reflect.TypeOf(*new(string)))
   rValue.Elem().SetString("hello world!")
   fmt.Println(rValue.Elem().Interface())
}
hello world!

Yapı

Yapı oluşturma da reflect.New fonksiyonunu kullanır

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() {
   // Yapı yansıma değerini oluştur
   rType := reflect.TypeOf(new(Person)).Elem()
   person := reflect.New(rType).Elem()
   fmt.Println(person.Interface())
}

Çıktı

{ 0  0}

Slice

Yansıma ile slice oluşturma

go
func MakeSlice(typ Type, len, cap int) Value
go
func main() {
   // Slice yansıma değerini oluştur
   rValue := reflect.MakeSlice(reflect.TypeOf(*new([]int)), 10, 10)
   // Yineleyerek değer ata
   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

Yansıma ile Map oluşturma

go
func MakeMapWithSize(typ Type, n int) Value
go
func main() {
   // Map yansıma değerini oluştur
   rValue := reflect.MakeMapWithSize(reflect.TypeOf(*new(map[string]int)), 10)
   // Değeri ayarla
   rValue.SetMapIndex(reflect.ValueOf("a"), reflect.ValueOf(1))
   fmt.Println(rValue.Interface())
}
map[a:1]

Kanal

Yansıma ile kanal oluşturma

go
func MakeChan(typ Type, buffer int) Value
go
func main() {
   // Kanal yansıma değerini oluştur
   makeChan := reflect.MakeChan(reflect.TypeOf(new(chan int)).Elem(), 0)
   fmt.Println(makeChan.Interface())
}

Fonksiyon

Yansıma ile fonksiyon oluşturma

go
func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value
go
func main() {
    // Paket türünü ve fonksiyon gövdesini gir
  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)})
}

Çıktı

func(int)
1024

Tamamen Eşit

reflect.DeepEqual, yansıma paketi tarafından sağlanan iki değişkenin tamamen eşit olup olmadığını belirlemek için kullanılan bir fonksiyondur. İmza aşağıdaki gibidir.

go
func DeepEqual(x, y any) bool

Bu fonksiyon her bir temel tür için işlem yapar. Aşağıda bazı türlerin yargılama yöntemleri bulunmaktadır.

  • Dizi: Dizideki her element tamamen eşittir
  • Slice: Her ikisi nil olduğunda, tamamen eşit olarak yargılanır. Veya her ikisi boş değilse, uzunluk范围内的 elementler tamamen eşittir
  • Yapı: Tüm alanlar tamamen eşittir
  • Harita: Her ikisi nil olduğunda, tamamen eşittir. Her ikisi nil değilse, her anahtarın eşlediği değer tamamen eşittir
  • İşaretçi: Aynı elementi işaret eder veya işaret edilen elementler tamamen eşittir
  • Arayüz: Arayüzün somut türü tamamen eşit olduğunda
  • Fonksiyon: Sadece her ikisi nil olduğunda tamamen eşittir, aksi takdirde tamamen eşit değildir

Aşağıda bazı örnekler bulunmaktadır:

Slice

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

Çıktı

true

Yapı

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

Çıktı

false
false
true

Golang by www.golangdev.cn edit