2017-03-08 18 views
3

有沒有一種方法可以使用Reflect訪問1.8版本中未導出的字段? 這似乎不再工作:https://stackoverflow.com/a/17982725/555493訪問golang中未導出的字段/反映?

注意reflect.DeepEqual作品就好了(也就是說,它可以訪問未導出字段),但我不能讓元首或功能的尾巴。下面是一個可以展示它的playarea:https://play.golang.org/p/vyEvay6eVG。在src代碼如下

import (
"fmt" 
"reflect" 
) 

type Foo struct { 
    private string 
} 

func main() { 
    x := Foo{"hello"} 
    y := Foo{"goodbye"} 
    z := Foo{"hello"} 

    fmt.Println(reflect.DeepEqual(x,y)) //false 
    fmt.Println(reflect.DeepEqual(x,z)) //true 
} 

回答

1

reflect.DeepEqual()可以爲valueInterface()功能,需要一個safe參數,它會拒絕未導出字段值訪問做到這一點,因爲它具有訪問reflect包的未導出的功能,在這種情況下即通過Value.Interface()方法如果safe=truereflect.DeepEqual()將(可能)稱爲通過safe=false

您仍然可以這樣做,但不能將Value.Interface()用於未導出的字段。相反,您必須使用類型特定的方法,例如Value.String()string,Value.Float()爲浮動,爲整數等Value.Int()這些將返回您的值的一個副本(這是足以檢查它),但不會允許您修改該字段的值(如果Value.Interface()可以工作並且字段類型是指針類型,則該值可能「部分」可能)。

如果某個字段恰好是接口類型,則可以使用Value.Elem()來獲取由接口值包含/包裝的值。

爲了證明:

type Foo struct { 
    s string 
    i int 
    j interface{} 
} 

func main() { 
    x := Foo{"hello", 2, 3.0} 
    v := reflect.ValueOf(x) 

    s := v.FieldByName("s") 
    fmt.Printf("%T %v\n", s.String(), s.String()) 

    i := v.FieldByName("i") 
    fmt.Printf("%T %v\n", i.Int(), i.Int()) 

    j := v.FieldByName("j").Elem() 
    fmt.Printf("%T %v\n", j.Float(), j.Float()) 
} 

輸出(嘗試在Go Playground):

string hello 
int64 2 
float64 3 
+0

好,但你怎麼能知道是什麼類型,這樣你可以調用正確的方法? –

+0

@UAvalos E.g.通過調用['Value.Type()'](https://golang.org/pkg/reflect/#Value.Type)(在字段的值上)。 – icza

+0

會不會導致一個非常痛苦的開關塊?例如:案例Uint,案例Uint8,Uint16等...... –

6

如果結構是可尋址的,你可以使用unsafe.Pointer訪問字段(讀或寫 )它像這樣:

rs := reflect.ValueOf(&MyStruct).Elem() 
rf := rs.Field(n) 
// rf can't be read or set. 
rf = reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())).Elem() 
// Now rf can be read and set. 

See full example on the playground.

根據unsafe包和go vet的文檔,unsafe.Pointer的這種用法是「有效的」。

如果結構不能尋址這一招是行不通的,但你可以創建一個可尋址複製這樣的:

rs = reflect.ValueOf(MyStruct) 
rs2 := reflect.New(rs.Type()).Elem() 
rs2.Set(rs) 
rf = rs2.Field(0) 
rf = reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())).Elem() 
// Now rf can be read. Setting will succeed but only affects the temporary copy. 

See full example on the playground.

+0

璀璨絕招,謝謝。結合'FieldByName',這是相當強大的,雖然很髒。 – user1112789

相關問題