2014-04-13 66 views
13

我想實現一個方法,它可以改變可以有任意結構的對象中的字段值。當我有指向結構體的指針時,字段的轉換是沒有問題的。但我不能設法改變字段時,我有一個不指針換到一個結構,但結構本身的接口,簡稱:Golang反射:無法設置包裝結構的接口字段

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

在長:http://play.golang.org/p/OsnCPvOx8F

我已閱讀The Laws of Reflection所以我知道當我有一個指向結構的指針時,我只能修改字段。所以我現在的問題是:如何獲得指向結構數據的指針?

UPDATE:

我得到它的工作基本上使用y := reflect.New(reflect.TypeOf(x))所以y值現在設定。有關廣泛的示例,請參閱:https://gist.github.com/hvoecking/10772475

回答

8

您似乎試圖修改存儲在接口變量中的動態值。您可以在接口變量上執行的唯一操作是獲取或設置動態值(進行復制的操作)以及檢查存儲值的類型。

要理解爲什麼事情是這樣,假設有這樣的操作,我們有下面的代碼:

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

是什麼ptr現在代表什麼?在爲接口變量分配新值時,該語言可以自由重複使用存儲,因此ptr現在可能指向B值的內存,這會打破語言的類型安全性(目前的編譯器存儲只能保證爲重複使用小值,但重點仍然存在)。

變異存儲在接口中的值的唯一安全方法是將值複製出來,然後分配修改後的版本。例如:

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

reflect的包反映了這些限制,因此代表了動態值的字段中的reflect.Value被視爲只讀。

您可以在第一個示例中設置字段,因爲z的動態值是*A指針,而不是結構本身:這意味着可以修改引用的結構。

+1

啊,我明白了。我解決這個問題的方法是(爲了支持任意結構)在y:= reflect.New(reflect.TypeOf(x))的幫助下創建一個新實例,並將所有'x'的字段複製到'y'的相應字段。 – Heye

+0

謝謝你,你救了我一個小時的搜索。 :) – Danyel

+0

這很有道理;但是爲什麼你不能在一個動態值中改變一個字段,例如「x。(list).head = z」 - 因爲這不會創建一個懸掛指針。 – WPWoodJr