Yansıtmayı kullanarak, bir yapı alanının değerini nasıl belirlersiniz?


107

reflectpaketi kullanarak yapı alanları ile çalışırken zor zamanlar geçiriyor . özellikle, alan değerinin nasıl ayarlanacağını bulamamışlardır.

t struct {fi int; fs dizesi}
var rt = t {123, "jblow"}
var i64 int64 = 456
  1. i alanının adını alma - bu işe yarıyor gibi görünüyor

    var field = reflect.TypeOf(r).Field(i).Name

  2. i alanının değerini a) arayüz {}, b) int olarak almak - bu işe yarıyor gibi görünüyor

    var iface interface{} = reflect.ValueOf(r).Field(i).Interface()

    var i int = int(reflect.ValueOf(r).Field(i).Int())

  3. i alanının ayar değeri - birini dene - panik

    reflect.ValueOf(r).Field(i).SetInt( i64 )

    panic : reflekte.Value · Dışa aktarılmamış alan kullanılarak elde edilen değeri kullanarak SetInt

    "id" ve "ad" alan adlarını beğenmediğini varsayarak, bu nedenle "Kimlik" ve "Ad" olarak yeniden adlandırıldı

    a) bu varsayım doğru mu?

    b) doğruysa, aynı dosya / pakette olduğu için gerekli olmadığı düşünülüyor

  4. i alanının değerini ayarlama - ikiyi deneyin (alan adları büyük harfle yazılır) - panik

    reflect.ValueOf(r).Field(i).SetInt( 465 )

    reflect.ValueOf(r).Field(i).SetInt( i64 )

    panic : reflektif.Value · SetInt adreslenemeyen değer kullanarak


@PeterSO tarafından aşağıdaki talimatlar eksiksiz ve kaliteli

Dört. bu çalışıyor:

reflect.ValueOf(&r).Elem().Field(i).SetInt( i64 )

alan adlarının dışa aktarılabilir olması gerektiğini de belgeler (büyük harfle başlar)


reflectVeri ayarlamak için kullanan biri için bulabildiğim en yakın örnek comments.gmane.org/gmane.comp.lang.go.general/35045 idi , ancak orada bile json.Unmarshalasıl kirli işi
cc young

(yukarıdaki yorum eski)
cc young

Yanıtlar:


157

Go, açık kaynak kodu olarak mevcuttur . Yansıtma hakkında bilgi edinmenin iyi bir yolu, çekirdek Go geliştiricilerinin onu nasıl kullandığını görmektir. Örneğin, Go fmt ve json paketleri. Paket belgeleri, Paket dosyaları başlığı altında kaynak kod dosyalarına bağlantılar içerir.

Go json paketi mareşal ve unmarshals JSON'dan ve Go yapılarına.


Burada, structhataları dikkatli bir şekilde önlerken bir alanın değerini ayarlayan adım adım bir örnek verilmiştir .

Go reflectpaketinin bir CanAddrişlevi vardır.

func (v Value) CanAddr() bool

Değerin adresi Addr ile elde edilebiliyorsa CanAddr true döndürür. Bu tür değerlere adreslenebilir denir. Bir dilimin bir öğesi, adreslenebilir bir dizinin bir öğesi, adreslenebilir bir yapının bir alanı veya bir göstericinin başvurusunu kaldırmanın sonucuysa, bir değer adreslenebilir. CanAddr yanlış döndürürse, Addr çağrısı paniğe neden olur.

Go reflectpaketinin bir CanSetişlevi vardır, eğer true, CanAddraynı zamanda öyle de demektir true.

func (v Value) CanSet() bool

V'nin değeri değiştirilebilirse CanSet true döndürür. Bir Değer, yalnızca adreslenebilirse ve dışa aktarılmamış yapı alanlarının kullanılmasıyla elde edilmediyse değiştirilebilir. CanSet yanlış döndürürse, Set veya herhangi bir türe özgü ayarlayıcı (örn. SetBool, SetInt64) çağırmak paniğe neden olacaktır.

Elimizden emin olmak gerekir alanı. Örneğin,Setstruct

package main

import (
    "fmt"
    "reflect"
)

func main() {
    type t struct {
        N int
    }
    var n = t{42}
    // N at start
    fmt.Println(n.N)
    // pointer to struct - addressable
    ps := reflect.ValueOf(&n)
    // struct
    s := ps.Elem()
    if s.Kind() == reflect.Struct {
        // exported field
        f := s.FieldByName("N")
        if f.IsValid() {
            // A Value can be changed only if it is 
            // addressable and was not obtained by 
            // the use of unexported struct fields.
            if f.CanSet() {
                // change value of N
                if f.Kind() == reflect.Int {
                    x := int64(7)
                    if !f.OverflowInt(x) {
                        f.SetInt(x)
                    }
                }
            }
        }
    }
    // N at end
    fmt.Println(n.N)
}

Output:
42
7

Tüm hata kontrollerinin gereksiz olduğundan emin olabilirsek, örnek basitleştiriyor:

package main

import (
    "fmt"
    "reflect"
)

func main() {
    type t struct {
        N int
    }
    var n = t{42}
    fmt.Println(n.N)
    reflect.ValueOf(&n).Elem().FieldByName("N").SetInt(7)
    fmt.Println(n.N)
}

5
Pes etmek! orada bir yerde bir cevap var, ancak json paketi üzerinde dört saatlik çalışma bunu bana vermedi. pkg'yi yansıtın, bilgiyi çekmek oldukça basittir, ancak verileri ayarlamak için bir yerde basit bir örnek görmek isteyeceğim biraz kara büyü gerekir!
cc genç

2
Muhteşem! Tayland'da olursanız, lütfen size bir veya iki veya üç bira ikram etmeme izin verin! çok teşekkür ederim
cc genç

5
Harika bir pratik örnek, bu makale benim için tamamen açıklığa kavuşturdu golang.org/doc/articles/laws_of_reflection.html
danmux

Harika bir örnek İşte aynı kodun oyun alanı örneği play.golang.org/p/RK8jR_9rPh
Sarath Sadasivan Pillai

Bu bir yapı alanı ayarlamaz. Bu, göstericideki bir alanı yapıya ayarlar. Açıkçası, bu OP'nin sorduğu soruyu yanıtlamıyor.
wvxvw

14

Bu işe yarıyor gibi görünüyor:

package main

import (
    "fmt"
    "reflect"
)

type Foo struct {
    Number int
    Text string
}

func main() {
    foo := Foo{123, "Hello"}

    fmt.Println(int(reflect.ValueOf(foo).Field(0).Int()))

    reflect.ValueOf(&foo).Elem().Field(0).SetInt(321)

    fmt.Println(int(reflect.ValueOf(foo).Field(0).Int()))
}

Baskılar:

123
321

Teşekkürler! PeterSO'nun notlarını okuduğuma göre, bu çok mantıklı. Ben & foo değil, foo kullanıyordum, bu yüzden değiştirilemezdim ve Elem () 'in ne hakkında olduğundan emin değildim.
cc genç
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.