私はコレクションと共分散を返すことに問題があり、誰かがより良い解決策を持っているかどうか疑問に思っていました。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つのリストを同期でき
- が、それは代わりのIListの
- リターンのIEnumerableを行うと、作成する方法を追加するための余分な作業のようです/削除コレクション
- は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であることを確認します。
これを試して、これが機能するかどうかを確認します。
オプション#2を使用すると、コード量は最小限に抑えられます。呼び出し側が必要としないIListの完全なサポートは、実装が責任を負うことがないため、インタフェースの表面積も減少します。 –
あなたのソリューションはうまく見えますが、なぜ 'ICollection'ではなく 'ICustomCollection 'を使っていますか? –
svick
私はまったく可能です。唯一の理由は、内部コードでアクセスする必要のある定期的なICollectionでは利用できないコレクタ用のメソッドがいくつかありますが、それは言及していません。 –
Khronos