2014-06-22 26 views
6

Parametre olarak iletilen bir arabirimin tüm alanları arasında yinelenen bir işlev var. Bunu başarmak için yansıma kullanıyorum. Sorun, işaretçi olmayan bir alanın adresini nasıl alacağımı bilmem. Burada bir örnek verilmiştir: Yukarıdaki kod, test yapılarımı temsil eder. BuradaYansıma değerini kullanarak işaretçiyi göster

func InspectStruct(o interface{}) { 
    val := reflect.ValueOf(o) 
    if val.Kind() == reflect.Interface && !val.IsNil() { 
     elm := val.Elem() 
     if elm.Kind() == reflect.Ptr && !elm.IsNil() && elm.Elem().Kind() == reflect.Ptr { 
      val = elm 
     } 
    } 
    if val.Kind() == reflect.Ptr { 
     val = val.Elem() 
    } 

    for i := 0; i < val.NumField(); i++ { 
     valueField := val.Field(i) 
     typeField := val.Type().Field(i) 
     address := "not-addressable" 

     if valueField.Kind() == reflect.Interface && !valueField.IsNil() { 
      elm := valueField.Elem() 
      if elm.Kind() == reflect.Ptr && !elm.IsNil() && elm.Elem().Kind() == reflect.Ptr { 
       valueField = elm 
      } 
     } 
     if valueField.Kind() == reflect.Ptr { 
      valueField = valueField.Elem() 
     } 
     if valueField.CanAddr() { 
      address = fmt.Sprint(valueField.Addr().Pointer()) 
     } 

     fmt.Printf("Field Name: %s,\t Field Value: %v,\t Address: %v\t, Field type: %v\t, Field kind: %v\n", typeField.Name, 
      valueField.Interface(), address, typeField.Type, valueField.Kind()) 

     if valueField.Kind() == reflect.Struct { 
      InspectStruct(valueField.Interface()) 
     } 
    } 
} 

Ve yapı örnekleme/başlatmasından sonra gerçek test edilir:

t := new(T) 
t.Id = 1 
t.F = *new(V) 
t.F.Id = 2 
t.F.F = *new(Z) 
t.F.F.Id = 3 

InspectStruct(t) 

Ve InspectStruct aramanın sonunda çıkış Şimdi burada belirtilen bir yapıya geçtiği ve bununla ilgili bilgileri listeler gerçek fonksiyonudur :

Field Name: Id, Field Value: 1,  Address: 408125440 , Field type: int , Field kind: int 
Field Name: F, Field Value: {2 {3}}, Address: 408125444 , Field type: main.V , Field kind: struct 
Field Name: Id, Field Value: 2,  Address: not-addressable , Field type: int , Field kind: int 
Field Name: F, Field Value: {3}, Address: not-addressable , Field type: main.Z , Field kind: struct 
Field Name: Id, Field Value: 3,  Address: not-addressable , Field type: int , Field kind: int 

alanlara bir tür ardından yapı ise bunun için InspectStruct çağrı yüzden özyinelemeyi kullanıyorum görebileceğiniz gibi. Benim sorunum, tüm alanlar "t" hiyerarşisi için tüm başlatılmış olsa da, "t" daha yüksek bir derinlikte bulunan herhangi bir alan için adres alamıyorum. Herhangi bir yardım için minnettar olurum.

cevap

8

valueField.Interface() çalışmıyor neden ancak Bilmiyorum, yerine interface{} ait reflect.Value sorunu çözmek gibi görünüyor geçirilmesi.

Çalışma örneği: http://play.golang.org/p/nleA2YWMj8

func InspectStructV(val reflect.Value) { 
    if val.Kind() == reflect.Interface && !val.IsNil() { 
     elm := val.Elem() 
     if elm.Kind() == reflect.Ptr && !elm.IsNil() && elm.Elem().Kind() == reflect.Ptr { 
      val = elm 
     } 
    } 
    if val.Kind() == reflect.Ptr { 
     val = val.Elem() 
    } 

    for i := 0; i < val.NumField(); i++ { 
     valueField := val.Field(i) 
     typeField := val.Type().Field(i) 
     address := "not-addressable" 

     if valueField.Kind() == reflect.Interface && !valueField.IsNil() { 
      elm := valueField.Elem() 
      if elm.Kind() == reflect.Ptr && !elm.IsNil() && elm.Elem().Kind() == reflect.Ptr { 
       valueField = elm 
      } 
     } 

     if valueField.Kind() == reflect.Ptr { 
      valueField = valueField.Elem() 

     } 
     if valueField.CanAddr() { 
      address = fmt.Sprintf("0x%X", valueField.Addr().Pointer()) 
     } 

     fmt.Printf("Field Name: %s,\t Field Value: %v,\t Address: %v\t, Field type: %v\t, Field kind: %v\n", typeField.Name, 
      valueField.Interface(), address, typeField.Type, valueField.Kind()) 

     if valueField.Kind() == reflect.Struct { 
      InspectStructV(valueField) 
     } 
    } 
} 

func InspectStruct(v interface{}) { 
    InspectStructV(reflect.ValueOf(v)) 
} 
10

Ben OneOfOne yayınındaki bir yorum olarak bu terk edecekti, ama biraz bir yorum için çok karmaşık ve soruya cevap çok alakalı ve çıktı neden OneOfOne cevabı doğru çalışıyor.

Interface() nedeninin nedeni, döndürdüğü arabirim sarıcısından kaynaklanmaktadır. Neler hakkında bir fikir vermek için, en biz yansıma olmadan ne yapıyoruz bakalım:

type MyStruct struct { 
    F Foo 
} 

type Foo struct { 
    i int 
} 

func ExtractField(ptr *MyStruct) interface{} { 
    return ptr.F 
} 

func main() { 
    ms := &MyStruct{Foo{5}} 
    f := ExtractField(ms).(Foo) // extract value 
    f.i = 19 
    fmt.Println(f, ms.F)   // ??? 
    fmt.Println(&f == &ms.F)  // Not the same! 
} 

(Playground)

Ancak interface{} bu getiriler düşünün. Sarma ne? Bu ptr.F değeri - yani kopyalayın. Bu, value.Interface'un yaptığı şeydir, alanı saran interface{} değerini döndürür. Artık herhangi bir işaretçi meta verisi yok, orijinal yapısından tamamen ayrılmış.

notu edeceğiniz gibi reflect.ValueOf için passing a value directly hep "üst katman" için CanAddr için false dönecektir - bu adres anlamsızdır, çünkü bu size değer kopyanın adresini verecekti beri değiştirmeyi olmazdı Gerçekten bir şey ifade etmiyor. (*Foo gibi bir işaretçi değerli alanın adresini istiyorsanız, **Foo'u gerçekten arıyorsanız) işaretçilerin değerler olduğunu unutmayın.biz safça reflect.ValueOf(ExtractField(ms)) geçmek olsaydı

Yani, yukarıdaki örneğimizde, biz istediğiniz adresi yok sadece ValueOff, almak istiyorum - çünkü yansıtmak için bile adreslenebilir göre değil hiçbir zaman numaralı adresi geçerli bir adres vermeyecektir (yansıtabileceği tek adres, Value yapısında dahili değer kopyasının adresidir).

Neden tavşan deliğinden Value geçişi çalışıyor? Elem ve Field'u kullanırken reflect.Value, reflect.Value, interface{} yapamazken gereken meta verileri koruduğunu söylemenin tek gerçek yolu. reflect.Value gibi görünse Yani:

// Disclaimer: not the real structure of a reflect.Value 
type Value struct { 
    fieldAddress uintptr 
    value  Foo 
} 

Tüm bu size @OneofOne bu

// Again, an abstraction of the real interface wrapper 
// just for illustration purposes 
type interface{} struct { 
    value Foo 
} 
+0

Bunun öğrendim, teşekkür: Bu değişiklikle birlikte

if valueField.Kind() == reflect.Struct { - InspectStruct(valueField.Interface()) + InspectStruct(valueField.Addr().Interface()) } 

, seni bekliyoruz çıkışı olsun. – OneOfOne

+1

@OneOfOne cevabını tamamladığınız için teşekkür ederiz. Çok takdir! –

1

Cevap mükemmel olduğunu verebilir, ancak bir ek, kontrol edildiğinde eklemek daha iyidir

if valueField.IsValid() { 
     fmt.Printf("Field Name: %s, Field Value: %v, Address: %v, Field type: %v, Field kind: %v\n", typeField.Name, 
      valueField.Interface(), address, typeField.Type, valueField.Kind()) 
    } 

bu gereklidir, çünkü bazen sıfır değerli bir yapıdan arayüz isteyebilirsiniz. Ones olur, panik olacak.

1

Bugün bir yansıtmalı tavşan deliğinden aşağı inmiştim, bu kodu ve Jsor'un cevabını okumaktan çok şey öğrendim, teşekkürler.

1) Başlangıçta işlevini çağırdığınızda bir yapı için bir gösterici geçiyoruz, ama ...

: Ben olsa belki daha basit çözüme ulaştırdığını sorun hakkında farklı bir sonuca vardık 2) 'InspectStruct (valueField.Interface())' diyerek tekrarladığınızda, gömülü yapıyı işaretçiye geçirmekten ziyade, bu değere göre iletirsiniz.

Değere göre geçiş yaptığınız için, git geçici olarak oluşturacak ve adresi almanıza izin vermeyecektir. Bunun yerine, yinelediğinizde, gömülü yapıya bir gösterici iletecek valueField.Addr(). Arabirim() öğesini çağırın. Detaylı cevap için

Field Name: Id, Field Value: 1,  Address: 842350527552 , Field type: int , Field kind: int 
Field Name: F, Field Value: {2 {3}}, Address: 842350527560 , Field type: lib.V , Field kind: struct 
Field Name: Id, Field Value: 2,  Address: 842350527560 , Field type: int , Field kind: int 
Field Name: F, Field Value: {3}, Address: 842350527568 , Field type: lib.Z , Field kind: struct 
Field Name: Id, Field Value: 3,  Address: 842350527568 , Field type: int , Field kind: int