2011-06-18 77 views
63

使用reflect包進行結構字段的處理時間很長。特別是還沒有想出如何設置字段值。使用反射,你如何設置結構字段的值?

 
type t struct { fi int; fs string } 
var r t = t{ 123, "jblow" } 
var i64 int64 = 456 
  1. 讓現場的名字,我 - 這似乎工作,我場的

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

  2. 獲得價值爲)接口{},B)INT - 這似乎工作

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

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

  3. 場的設定值我 - 一個嘗試 - 恐慌

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

    恐慌:使用值獲得使用未導出場

    假設它不喜歡的領域reflect.Value·SETINT名稱「id」和「name」,所以改名爲「Id」和「Name」

    a)這個假設是否正確?

    B)如果正確的話,認爲沒有必要,因爲在同一個文件/封裝領域的

  4. 設定值i - 嘗試兩(字段名爲大寫) - 恐慌

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

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

    panic:reflect.Value·SetInt using unaddressable value


說明低於@peterSO是全面和高品質

四。這個工程:

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

他文件,以及該字段名稱必須導出(開頭大寫字母)

+0

我能找到的最接近的例子有人用'reflect'來設置數據是http://comments.gmane.org/gmane.comp.lang.go.general/35045,但即使在那裏,他也使用'json.Unmarshal'來做實際的骯髒工作 –

+0

(the以上評論已過時) –

回答

93

圍棋可以作爲open source code。瞭解反思的一個好方法是看看核心Go開發人員如何使用它。例如,Go fmtjson包。包文檔具有指向包文件標題下的源代碼文件的鏈接。

Go json包封送和取消編組來自Go結構的JSON。


這裏的一個步驟一步示例,其設定struct字段的值,同時小心避免錯誤。

Go reflect包具有CanAddr功能。如果該值的 地址可以用地址獲得

func (v Value) CanAddr() bool 

CanAddr返回true。 這樣的值被稱爲可尋址的。如果 值是片段的 元素,可尋址數組的 元素,可尋址結構的 字段或取消引用指針的 的結果,則該值是可尋址的。如果CanAddr 返回false,則調用Addr將發生 恐慌。

轉到reflect封裝具有CanSet功能,而如果true,意味着CanAddrtrue。如果V 的值可以改變

func (v Value) CanSet() bool 

CANSET返回true。只有在可尋址的情況下才能更改值 ,並且使用未導出的 結構字段獲取的值不是 。如果CanSet返回false,則調用Set或任何類型特定的setter(例如SetBool, SetInt64)將會出現混亂。

我們需要確保我們可以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 

如果我們能肯定的是,所有的錯誤檢查是不必要的,示例簡化爲,

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

現在去那裏 –

+2

放棄!某處有一個答案,但是json pkg上的四個小時工作並沒有讓我知道。反映pkg,拉動信息是非常簡單的,但設置數據需要一些黑魔法,我很想看到一個簡單的例子! –

+2

傑出!如果你曾經在泰國,請讓我對你喝一兩杯啤酒!非常感謝你 –

6

這似乎工作:

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

打印:

123 
321 
+0

謝謝!現在我已經閱讀了peterSO的筆記,這是非常有意義的。我使用的是foo,而不是foo,因此無法更改,並且不確定Elem()的內容。 –