2014-04-13 11 views
13

Ich versuche eine Methode zu implementieren, die den Wert von Feldern in einem Objekt ändert, das eine beliebige Struktur haben kann. Die Traversion der Felder ist kein Problem, wenn ich den Zeiger auf eine Struktur habe. Aber ich kann es nicht schaffen, die Felder zu ändern, wenn ich eine Schnittstelle haben, die nicht einen Zeiger auf eine Struktur umbrochen, sondern die Struktur selbst, kurz:Golang-Reflektion: Felder der Schnittstelle, die eine Struktur umschließt, können nicht gesetzt werden

// The following doesn't work 
var x interface{} = A{Str: "Hello"} 
// This panics: reflect: call of reflect.Value.Field on ptr Value 
reflect.ValueOf(&x).Field(0).SetString("Bye") 
// This panics: reflect: call of reflect.Value.Field on interface Value 
reflect.ValueOf(&x).Elem().Field(0).SetString("Bye") 
// This panics: reflect: reflect.Value.SetString using unaddressable value 
reflect.ValueOf(&x).Elem().Elem().Field(0).SetString("Bye") 
// This prints `false`. But I want this to be settable 
fmt.Println(reflect.ValueOf(&x).Elem().Elem().Field(0).CanSet()) 

// This works 
var z interface{} = &A{Str: "Hello"} 
// This prints `true` 
fmt.Println(reflect.ValueOf(z).Elem().Field(0).CanSet()) 

In lang: http://play.golang.org/p/OsnCPvOx8F

Ich habe The Laws of Reflection Ich bin mir bewusst, dass ich Felder nur ändern darf, wenn ich einen Zeiger auf eine Struktur habe. Also meine Frage ist jetzt: Wie bekomme ich den Zeiger auf die Daten der Struktur?

UPDATE:

Ich habe es funktioniert im Grunde mit y := reflect.New(reflect.TypeOf(x)) so die Werte von y sind jetzt einstellbar. Ein ausführliches Beispiel finden Sie unter: https://gist.github.com/hvoecking/10772475

Antwort

8

Sie scheinen zu versuchen, den dynamischen Wert zu ändern, der in einer Schnittstellenvariablen gespeichert ist. Die einzigen Operationen, die Sie für eine Schnittstellenvariable ausführen können, sind das Abrufen oder Festlegen des dynamischen Werts (Vorgänge, die Kopien erstellen) und das Überprüfen des Typs des gespeicherten Werts.

Um zu verstehen, warum die Dinge so sind, vorstellen, dass es eine solche Operation war und wir hatten den folgenden Code:

var ptr *A = pointer_to_dynamic_value(x) 
x = B{...} 

Was stellt noch ptr jetzt? Die Sprache kann Speicher bei der Zuweisung neuer Werte zu einer Schnittstellenvariablen frei verwenden, so dass der ptr jetzt auf den Speicher für den B Wert verweist, der die Typensicherheit der Sprache durchbricht (wobei der aktuelle Compilerspeicher nur garantiert ist) für kleine Werte wiederverwendet, aber der Punkt bleibt).

Die einzige sichere Möglichkeit, den in einer Schnittstelle gespeicherten Wert zu ändern, besteht darin, den Wert zu kopieren und dann die geänderte Version zurückzugeben. Zum Beispiel:

a := x.(A) 
a.Str = "Bye" 
x = a 

Das reflect Paket spiegelt diese Einschränkungen, so wird die reflect.Value das Feld des dynamischen Wertes, der nur gelesen betrachtet.

Sie können Felder in Ihrem ersten Beispiel festlegen, da der dynamische Wert für z ein Zeiger *A lieber als die Struktur selbst ist: das bedeutet, dass die referenzierte Struktur geändert werden kann.

+1

Ah, ich verstehe es. Mein Weg um dieses Problem wäre (um beliebige Strukturen zu unterstützen), eine neue Instanz mit Hilfe von 'y: = reflect.New (reflect.TypeOf (x))' zu erstellen und alle Felder von 'x' in die zu kopieren entsprechende Felder von 'y'. – Heye

+0

Danke Heye, du hast mich eine weitere Stunde der Suche gerettet. :) – Danyel

+0

Das macht vollkommen Sinn; Aber was ist der Grund dafür, warum Sie ein Feld in einem dynamischen Wert nicht ändern können, zum Beispiel "x. (list) .head = z" - da dies keinen ungeeigneten Zeiger erzeugt. – WPWoodJr

Verwandte Themen