2012-03-15 101 views
5

我有以下接口聲明:如何從C#中的通用接口的子對象引用父對象?

interface IOrder<T> where T: IOrderItem 
{ 
    IList<T> Items { get; set; } 
} 

interface IOrderItem 
{ 
    IOrder<IOrderItem> Parent { get; set; } // What do I put here? 
} 

我想在列表中的項目以具有與標題對象的引用,因此它可以使用該ID等領域從報頭。

在我的具體類中,它抱怨我沒有正確實現「Parent」。

class StoreOrder : IOrder<StoreOrderItem> 
{ 
    public IList<StoreOrderItem> Items { get; set; } 
} 

class StoreOrderItem : IOrderItem 
{  
    public StoreOrder Parent { get; set; } // This doesn't satisfy the interface 
} 

我試圖建立IOrderItemIOrderItem<T>並傳入父類型,但導致自頭班循環引用requries Item類類型......我弄糊塗了。

任何關於如何正確實施的建議?

+0

[這可能對你有意思](http://msdn.microsoft.com/en-us/library/dd469487.aspx) – abatishchev 2012-03-15 18:54:33

回答

3

如果你這樣定義你的接口:

interface IOrder<T> where T : IOrderItem<T> 
{ 
    IList<T> Items { get; set; } 
} 
interface IOrderItem<T> where T : IOrderItem<T> 
{ 
    IOrder<T> Parent { get; set; } 
} 

然後,您可以實現它們像這樣得到您所期望的功能:

class StoreOrder : IOrder<StoreOrderItem> 
{ 
    public IList<StoreOrderItem> Items { get; set; } 
} 
class StoreOrderItem: IOrderItem<StoreOrderItem> 
{ 
    public IOrder<StoreOrderItem> Parent { get; set; } 
} 
+0

這樣可以糾正編譯器錯誤,但會將提示器出現的問題解析爲「Parent」屬性的類型,而不是「StoreOrder」而退回到「IOrder 」。因此,如果你有一個'StoreOrderItem'並且想用'Parent'做任何事情,除了在IOrder .Items'屬性上操作,你必須首先將'Parent'轉換爲'StoreOrder'。 – devgeezer 2012-03-19 17:51:59

+2

@devgeezer - 你是對的。但是,我的實現將允許任何'IOrder '實現爲父級。任何'IOrderItem '需要知道它的父節點應該在'IOrder '接口中實現,因爲兩者之間的關係是在接口級而不是實現級定義的。 – 2012-03-19 18:29:38

+0

優秀點。我在想,我對這個問題讀了太多意圖。 +1 – devgeezer 2012-03-20 02:19:33

1
class StoreOrder : IOrder<StoreOrderItem> 
{ 
    public int Id { get; set; } 
} 

class StoreOrderItem : IOrderItem 
{  
    public IOrder<IOrderItem> Parent { get; set; } // This doesn't satisfy the interface 
} 

你可能不專注 - IOrder<IOrderItem>StoreOrder

+0

爲什麼ID?爲什麼沒有'IOrder .IList '? – abatishchev 2012-03-15 18:43:06

+0

你是對的,不知道爲什麼。編輯。 – IronicMuffin 2012-03-15 18:52:54

+0

@Eugen:你的代碼是否可編譯? – abatishchev 2012-03-16 08:24:38

0

宣言更廣泛的滿足接口:

class StoreOrder : IOrder<StoreOrderItem> 
{ 
    // interface members 
    public IList<StoreOrderItem> Items { get; set; } 

    // own members 
    public int Id { get; set; } 
} 

class StoreOrderItem : IOrderItem 
{ 
    public IOrder<IOrderItem> Parent { get; set; } 
} 

若要訪問自定義成員,你將不得不投:

class StoreOrderItem : IOrderItem 
{ 
    void Test() 
    { 
     int id = ((StoreOrder)this.Parent).ID; 
    } 
} 
1

下面是接口變化的解決方案:

interface IOrder<TOrder, TOrderItem> 
    where TOrderItem : IOrderItem<TOrder> 
{ 
    IList<TOrderItem> Items { get; set; } 
} 

interface IOrderItem<TOrder> 
{ 
    TOrder Parent { get; set; } 
} 

進行更改StoreOrderStoreOrderItem以支持接口更改爲以後的測試添加了一些屬性:

class StoreOrder: IOrder<StoreOrder, StoreOrderItem> 
{ 
    public DateTime Date { get; set; } 
    public IList<StoreOrderItem> Items { get; set; } 
} 

class StoreOrderItem : IOrderItem<StoreOrder> 
{ 
    public string ItemName { get; set; } 
    public decimal ItemPrice { get; set; } 
    public StoreOrder Parent { get; set; } 
} 

...現在創建StoreOrderStoreOrderItem實例,並把他們通過他們的步伐:

void Main() 
{ 
    var so = new StoreOrder { Date = DateTime.Now }; 
    var item = new StoreOrderItem { 
      Parent = so, 
      ItemName = "Hand soap", 
      ItemPrice = 2.50m }; 
    so.Items = new [] { item }; 

    Console.WriteLine(item.Parent.Date); 
    Console.WriteLine(so.Items.First().ItemName); 
} 

...運行時,印刷:

3/16/2012 10:43:55 AM 
Hand soap 

另一種選擇是要取消上述並採取this solution並通過添加具有所需類型的Parent屬性並使用顯式接口實現來更改它,以避免在呼叫站點投射,鬧了StoreOrderItem實現是這樣的:

class StoreOrderItem : IOrderItem 
{ 
    public string ItemName { get; set; } 
    public decimal ItemPrice { get; set; } 
    public StoreOrder Parent { get; set; } // note: original implementation 

    IOrder<IOrderItem> IOrderItem.Parent { // explicit interface implementation 
     get { return (IOrder<IOrderItem>)this.Parent; } 
     set { this.Parent = (StoreOrder)value; } 
    } 
} 

我上面的最愛是以上兩個泛型參數IOrderIOrderItem不受約束的泛型參數的第一個建議。之前發佈的版本和現在編輯的版本都具有相同的兩個通用類型,每個通用類型具有相同的約束。我覺得這有點太過分了,所以我削減了上面的實現。儘管在TOrder類型參數到IOrderItem之間完全沒有約束 - 試圖在其位置使用其他類型(例如,object)導致編譯錯誤。使用TOrder而不是僅僅調用它,T在沒有類型約束的情況下提供了有關預期類型的​​提示。這將是我的最終編輯 - 我覺得這是我嘗試的最簡潔的方式;如果你很好奇,我可以提供在接口上具有雙通用約束類型的前一個實現,但這至少是我最喜歡的這種解決方案。乾杯!

+0

我已經瞭解到我的第一個解決方案有一個名稱:「好奇地重複出現的模板模式」。 SO標籤[ctrp]。 http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern – devgeezer 2012-03-19 17:55:31