2011-09-11 10 views
9

私はコレクションと共分散を返すことに問題があり、誰かがより良い解決策を持っているかどうか疑問に思っていました。C#でコレクションを返すときの共分散を処理する方法は?

シナリオはこれです:

私は、実装の2バージョンを持っていると私は(彼らは同じロジックを持っている可能性があるにもかかわらず)完全に別のバージョンの実装を維持したいと思います。実装では、アイテムのリストを返すので、インターフェイスではアイテムのインターフェイスのリストを返します。しかし、インターフェイスの実際の実装では、私はアイテムの具体的なオブジェクトを返すしたいと思います。コードでは、このように見えます。

interface IItem 
{ 
    // some properties here 
} 

interface IResult 
{ 
    IList<IItem> Items { get; } 
} 

そして、これらのインタフェースの具体的な実装を有する2つの名前空間が存在することになります。例えば、

名前空間バージョン1

class Item : IItem 

class Result : IResult 
{ 
    public List<Item> Items 
    { 
     get { // get the list from somewhere } 
    } 

    IList<IItem> IResult.Items 
    { 
     get 
     { 
      // due to covariance, i have to convert it 
      return this.Items.ToList<IItem>(); 
     } 
    } 
} 

は、名前空間のバージョン2の下で、同じことの別の実装があるでしょう。

これらのオブジェクトを作成するには、バージョンを取得し、必要に応じて適切な具象タイプを作成するファクトリがあります。

呼び出し側が正確なバージョンを知っていて、次の処理を行い場合は、コードが正常に動作し

Version1.Result result = new Version1.Result(); 
result.Items.Add(//something); 

しかし、私は、ユーザーがこのような何かをできるようにしたいと思います。

IResult result = // create from factory 
result.Items.Add(//something); 

ただし、別のリストに変換されているため、アイテムは元の結果オブジェクトに追加されないため、追加処理は行われません。

私は、次のようないくつかの解決策を考えることができます:私は2つのリストを同期でき

  1. が、それは代わりのIListの
  2. リターンのIEnumerableを行うと、作成する方法を追加するための余分な作業のようです/削除コレクション
  3. はTConcreteとTInterface

私はこれが(原因タイプセーフとすべてに)起こっている理由を理解を取るカスタムコレクションを作成しますが、workarounのどれも私はとても優雅に見えると思います。誰かより良い解決策や提案がありますか?

ありがとうございます!呼び出し元がある場合、私は、

interface IResult 
{ 
    ICustomCollection<IItem> Items { get; } 
} 

then for implementation, I will have 

class Result : IResult 
{ 
    public CustomCollection<Item, IItem> Items { get; } 

    ICustomCollection<TItem> IResult.Items 
    { 
     get { return this.Items; } 
    } 
} 

その方法を持つことができ、その後

public interface ICustomCollection<TInterface> : ICollection<TInterface> 
{ 
} 

public class CustomCollection<TConcrete, TInterface> : ICustomCollection<TInterface> where TConcrete : class, TInterface 
{ 
    public void Add(TConcrete item) 
    { 
     // do add 
    } 

    void ICustomCollection<TInterface>.Add(TInterface item) 
    { 
     // validate that item is TConcrete and add to the collection. 
     // otherwise throw exception indicating that the add is not allowed due to incompatible type 
    } 

    // rest of the implementation 
} 

更新

この以上のことを考えた後、私は次の操作を行うことができると思いますResultクラスにアクセスすると、すでにTConcreteであるCustomCollection.Add(TConcreteアイテム)を通過します。呼び出し元がIResultインターフェイスを介してアクセスしている場合、customCollection.Add(TInterface項目)を通過し、検証が行われ、そのタイプが実際にTConcreteであることを確認します。

これを試して、これが機能するかどうかを確認します。

+1

オプション#2を使用すると、コード量は最小限に抑えられます。呼び出し側が必要としないIListの完全なサポートは、実装が責任を負うことがないため、インタフェースの表面積も減少します。 –

+0

あなたのソリューションはうまく見えますが、なぜ 'ICollection 'ではなく 'ICustomCollection 'を使っていますか? – svick

+0

私はまったく可能です。唯一の理由は、内部コードでアクセスする必要のある定期的なICollection では利用できないコレクタ用のメソッドがいくつかありますが、それは言及していません。 – Khronos

答えて

2

あなたが直面している問題は、(他のものの中でも)「あなたは私にIItemを追加できます」というタイプを公開したいからですが、実際には「Itemを私に追加できます」 。私は最高の解決策は実際にIList<Item>を公開することだと思う。とにかく、それをリストに追加する必要がある場合は、これを使用するコードで具体的なItemを知る必要があります。

本当にこれをやりたければ、私が正しく理解すれば、最もクリーンな解決策は3となると思います。 IList<TConcrete>のラッパーになり、IList<TInterface>を実装します。 TConcreteではないものを入れようとすると、例外がスローされます。

0

+1ブレントのコメント:変更不可能なコレクションを返し、クラスの変更方法を提供します。あなたが説明可能な方法で1つの作業得ることができない理由だけではなく、繰り返しに

:あなたは、単にIItemを実装していますが、タイプ(または由来)ではないList<Item>要素に追加しよう

をあなたはされません項目この新しいアイテムをリストに格納することができます。結果のインターフェイスの動作が非常に矛盾するため、IItemを実装する要素の中にはうまく追加できますが、simeは失敗し、実装をversion2に変更すると、動作も変わります。

IList<IItem> insiteadをList<Item>のように保存するのが簡単な修正ですが、コリジョンを直接公開するには慎重な考えが必要です。

0

問題はMicrosoftがIList <T>という1つのインターフェイスを使用して、追加できるコレクションと追加できないコレクションの両方を記述することです。理論的には、クラスがIList <Cat>を変更可能に実装し、IList Animal >を実装することは理論的には可能ですが(前者のインタフェースのReadOnlyプロパティはfalseを返し、後者はtrueを返します)、 IList <Cat>片道、およびIList <T>を実装することを指定するクラスcat:T、別の方法。私は、Microsoftがこれらの共変性と反変性のために許可されていたので、T >はT >、T >、IReadWriteByIndex <T>、T >、およびICountableでIAppendable <、中IWritableByIndex <からIReadableByIndex <を実装するIList <T>一覧<をしていた希望しますしかし、彼らはしなかった。共分散が役立つ程度に応じて、このようなインターフェースを自分で実装し、それらのラッパーを定義すると便利です。

関連する問題