2014-02-28 69 views
3

我有這樣的演示數據結構。正如你所看到的,foo有一個嵌入式指針bar反映認爲struct Value也是ptr?

type foo struct { 
    *bar 
} 

type bar struct { 
    S []byte 
} 

而且我使用的reflect包這樣的:

func test(x interface{}) { 

    var v = reflect.ValueOf(x) 

    if v.Kind() == reflect.Struct { 
     fmt.Println("was a struct") 

    // panic: reflect: call of reflect.Value.Elem on struct Value 
    // v = v.Elem() 

    // panic: reflect: call of reflect.Value.Field on ptr Value 
     v = v.FieldByName("S") 
    } 
} 

func main() { 
    var f foo 
    test(f) 
    fmt.Println(string(f.S)) 
} 

所以v.Kind()是公認的reflect.Struct,但如果我嘗試通過使用.FieldByName("S")來將它當作一個結構對待,它會因爲它認爲vptr而發生混亂。

所以後來如果我嘗試致電.Elem()把它當作一個ptr,其恐慌,因爲它認爲v是一個struct

我試過reflect.Indirect(),以及其他一些事情,但我不知道如何獲得嵌入式指針的字段。

有沒有辦法將reflect.Value表示從嵌入式指針轉換爲結構?

演示:http://play.golang.org/p/n0eea6XW3I


編輯:也試過v = v.FieldByName("bar"),但得到:

panic: runtime error: invalid memory address or nil pointer dereference

+0

我必須查看文件以確保,但我的直覺是,可能是嵌入式類型混淆它。 – LinearZoetrope

+0

這絕對是令人困惑的嵌入式類型。嘗試更改欄不是一個指針,它不會崩潰。修改:刪除對f.S的引用,併爲*欄指定一個名稱並且它可以工作。我正在瀏覽文檔以查看是否在任何地方提及此行爲。 – LinearZoetrope

+0

@Jsor:謝謝,是的,我可以看到那裏會有些混亂。 「反射」中找不到任何特別處理嵌入式指針的內容。 –

回答

6

我們需要認識的第一件事是var f foo行等於f := foo{}。這將內部字段bar(類型* bar)初始化爲其零值...零。嵌入類型的行爲和反映似乎是它將嵌入類型的字段視爲類型本身的字段。所以當你請求v.FieldByName("S")時,它試圖在f的成員bar中找到該字段,它是nil。

您正在嘗試這樣做(*f.bar).S。 (在Go中不需要顯式指針解引用,但它使我的觀點)。現在的問題是:如果您更改爲v.FieldByName("bar")它爲什麼會出現錯誤?同樣的道理。

仔細查看堆棧跟蹤,FieldByName行不再崩潰,崩潰的行是fmt.Println(string(f.S))。同樣,在語義上你正在做(*f.bar).S。但是成員「bar」是零,所以你實際上做了一個零指針解引用。

您可以通過將var f foo更改爲f := foo{&bar{}}來解決這兩個錯誤。

+0

啊,是的,謝謝。這就說得通了。只是恐慌的信息令人困惑。完全有道理。非常感激! –

+2

@cookiemonster錯誤消息肯定是令人困惑的。它應該真的說「試圖解除引用零指針」,而不是給你怪異的「reflect.Field on pointer」錯誤。 – LinearZoetrope

0

我被收到這個錯誤 「恐慌:反映:上結構值reflect.Value.Elem的呼叫」 這一行 「reflect.ValueOf(parameterName)以.Elem()」

1的BCZ。當我使用反射ELEM(),這意味着參數名稱中的valueOf()應該是一個指向結構

func Search(flight interface{}, key string) string { 

    val := reflect.ValueOf(flight).Elem() 

    for i := 0; i < val.NumField(); i++ { 
    valueField := val.Field(i) 
    typeField := val.Type().Field(i) 
    if key == strings.ToLower(typeField.Name) { 
     return valueField.Interface().(string) 
    } 
    } 
    return "" 
} 

現在在調用搜索功能,我的電話應該是這樣的! 結果:=搜索(&航班,關鍵)

相關問題