2012-03-15 1 views
5

Iは、次のインターフェイス宣言を有する: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及び他のフィールドを使用することができるので、私は、リスト内の項目は、ヘッダオブジェクトへの参照を持っていたいです。

具体的なクラスでは、「親」を適切に実装していないと文句を言います。

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

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

ヘッダークラ​​スはItemクラスの種類をrequries以来、私は混乱しました... IOrderItem<T>としてIOrderItemを設定し、親の型を渡してみましたが、循環参照へのリード。

これを正しく実装する方法についてのアドバイスはありますか?

+0

[これは面白いかもしれません](http://msdn.microsoft.com/en-us/library/dd469487.aspx) – abatishchev

答えて

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

これは、コンパイラエラーを修正しますが、質問者が試行していたように、 'StoreOrder'の代わりに' Parent'プロパティのタイプを 'IOrder 'に戻します。だから、 'StoreOrderItem'を持っていて、' IOrder .Items'プロパティを操作する以外に 'Parent'を使って何かしたいのであれば、まず' Parent'を 'StoreOrder'にキャストしなければなりません。 – devgeezer

+2

@devgeezer - あなたは正しいです。しかし私の実装では、 'IOrder '実装を親にすることができます。 'IOrderItem 'がその親について知る必要があるものは、 'IOrder 'インタフェースで実装する必要があります。これは、2つの間の関係が実装レベルではなくインタフェースレベルで定義されているからです。 –

+0

優秀な点。私は質問にあまりにも多くの意図を読んでいると思っています。 +1 – devgeezer

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

+0

あなたは正しいのですが、理由が分かりません。編集されました。 – IronicMuffin

+0

@Eugen:あなたのコードはコンパイル可能ですか? – abatishchev

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インスタンスを作成し、そのペースを介してそれらを置く:

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

別のオプションです:実行、印刷が

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

...上記を破棄して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; } 
    } 
} 

私のお気に入りはIOrderには、2つの汎用パラメータとIOrderItem上の制約のない一般的なパラメータと上記の最初の提案です。私が掲示して編集した以前のバージョンは、それぞれ同じ制約を持つ同じ2つのジェネリックタイプを持つ両方のインターフェースを持っていました。私はこれが少し外に出ているように感じたので、上記の実装に戻してみました。 TOrder型パラメータの制約が全くないにもかかわらず、IOrderItemの型パラメータに置き換えられました。他の型の代わりにその型を変更しようとすると(たとえば、object)、コンパイルエラーが発生しました。 Tを呼び出す代わりにTOrderを使用すると、型制約がない場合に期待される型に関するヒントが得られます。それが私の最終編集です - 私はそれが私の試みの中で最も簡潔だと感じます。面白いと思うなら、私はインターフェイス上に二重ジェネリック制約付きの型を持っていた前の実装を提供することができますが、これは少なくとも私の好みのこの解決策です。乾杯!

+0

私は最初の解決策に「奇妙な反復テンプレートパターン」という名前があることを学びました。 SOタグ[ctrp]。 http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern – devgeezer

関連する問題