2013-10-11 167 views
1

(作爲後續這個問題:nested struct initialization literals)。嵌套結構 - 獲得「基」結構

既然我可以初始化一個易於編寫的文字的結構,我後來在我的代碼中需要訪問父結構的成員,但不知道具體的派生類型。它是這樣的:

type A struct { 
    MemberA string 
} 

type B struct { 
    A 
    MemberB string 
} 

然後我用這樣的:

b := B { 
    A: A { MemberA: "test1" }, 
    MemberB: "test2", 
} 
fmt.Printf("%+v\n", b) 

var i interface{} = b 

// later in the code, I only know that I have something that has a nested A, 
// I don't know about B (because A is in a library and B is in the 
// calling code that uses the library). 

// so I want to say "give me the A out of i", so I try a type assertion 

if a, ok := i.(A); ok { 
    fmt.Printf("Yup, A is A: %+v\n", a) 
} else { 
    fmt.Printf("Aristotle (and John Galt) be damned! A is NOT A\n") 
} 

// no go 

選項我看到的是:

  • 我可以使用反射來尋找一個名爲「成員一個「,並假設它是正確的類型,使用它。這將是可行的,但效率較低,肯定更「笨拙」。

  • 我可能需要調用者實現接口(如HasA { Aval() A }或類似返回A的一個實例到目前爲止,這是最好的想法,我能想到的。

  • 另一點是,我可以只是讓調用者傳遞一個A值(即在上面的例子中,var i interface{} = b變成了var i A = b.A)但是,發生的事情是我實際上動態地遍歷B的成員並且對它們做了一些事情,所以我需要更多的「派生」類型(我忽略了這個問題,因爲它更像是背景,爲什麼我會遇到這個問題,並且與問題的技術答案無關。)

如果我可以「將它投射到A」,那將是非常好的,就像在Java中一樣。有沒有更好的方法來做到這一點。

回答

2

這將是巨大的,如果我能「將它轉換爲A」,因爲你會在Java中。有沒有更好的方法來做到這一點。

盲鑄造幾乎總是對代碼質量的壞消息因此它實際上是相當不錯的 使其很難做到這一點。

我會去接口路由,因爲它也會消除您用來存儲binterface{}。 如果你確實有很高的多種類型,他們只分享他們嵌入A接口 它提供AVal() A似乎對我好。

作爲獎勵,您可以直接在A上定義AVal() A,因此您無需爲嵌入A的每種類型實施它。

例(on play):

type A struct { 
    MemberA string 
} 

func (a *A) AVal() *A { 
    return a 
} 

type B struct { 
    *A 
    MemberB string 
} 

type AEmbedder interface { 
    AVal() *A 
} 

func main() { 
    b := B { 
      A: &A { MemberA: "test1" }, 
      MemberB: "test2", 
    } 

    var i AEmbedder = b 

    a := i.AVal() 
    fmt.Printf("Yup, A is A: %+v\n", a) 
} 

請注意,我現在用的是指針值,這樣AVal() *A不返回副本,但 的A各自的實例。

+0

我明白了。由於A實現了AEmbedder,所以B,通過嵌套也可以實現......這看起來非常有效且簡單。這也意味着當你執行'var i AEmbedder = b'時,'i'中的值的運行時類型仍然是B - 所以迭代它的字段等,給我更多派生時間的結果。漂亮的拉德。我認爲這正是我想要的。 (除非我完全可以避免這一點,正如你所提到的。) –

0

這對我有效。雖然它不是像你想要的那樣直接鑄造,但它確實給出了正確的對象實例

fmt.Printf("It's", i.(B).A) 

我希望它有幫助!

2

如果你有一個未知的類型b,挖掘嵌入字段的唯一方法是通過反射。

這不是笨拙:

// obviously missing various error checks 
t := reflect.ValueOf(i) 
fmt.Printf("%+v\n", t.FieldByName("A").Interface().(A)) 

結構嵌入是繼承,並試圖用它作爲這樣將會繼續爲大家帶來了這樣的問題。在go中實現通用多態的方法是使用接口。

我認爲處理這種情況的最簡潔的方法是使用一個通用接口,併爲要處理的字段使用適當的訪問器方法。你會看到這個stdlib的例子,例如http.ResponseWriter,它有一個Header()方法用於訪問實際的響應標頭。

+0

感謝吉姆 - 你說得對。問題本身並試圖通過嵌套結構來解決它比解決方案本身更笨拙 - 這個片段在給出問題的情況下相當易於管理。在標準接口模式的其他用途上了解,這也是一個很好的觀點。 –

0

玩了了一下,我得到這個工作:http://play.golang.org/p/-bbHZr-0xx

我不是爲什麼這個工程肯定100%,但我的最好的理論是,當你調用

a := i.(A) 

你正在試圖做的一切存儲在iA類型轉換的接口版本。所以,你首先需要告訴它,它實際上是B,那麼你就可以訪問被嵌套在一家IT

a := i.(B).A 
+0

你用'我(B)'斷言'我'是'B'。之後,您可以從該聲明值請求'A',因爲它是'B'的成員。這裏沒有魔法。可悲的是,這要求你爲每種可能的類型測試'i',因爲'B'可能不是唯一具有'A'的類型。因此這種方法可能不是最好的。 – nemo

+0

@nemo - 是的,這正是我遇到的難題。我不想讓該代碼「知道」B,因爲B實際上是在另一個使用A作爲庫存在的包中創建的包中創建的。 –

+0

最好的選擇是a)避免這種情況,b)使用接口c)使用反射(按照該順序)。 – nemo