3

私は興味深い問題を抱えていますが、私は静かに解決策を見つけているようには見えません。

私は防御的なプログラマーになる傾向があるので、問題が発生してから対処するのではなく、問題が発生しないようにするコードを書くようにしています。そのために、私は次のような状況にあります。次のコードを使用します。

public class Base {}
public Interface IBase {}

public class Derived : Base, IBase {}
public class Derived2 : Base, IBase {}
...
public class DerivedN : Base, IBase {}

public class X : Base {}
public class Y : IBase {}

Base から派生し、IBase を実装するオブジェクトのリストをコレクションに渡す必要があり、両方を持つオブジェクトのみがリストに追加されるようにする必要があります。さらに、両方を持つ任意の数のクラスが存在する可能性があるため、派生クラスを制約として使用することはできません。

Base 型のリストを作成すると、Y オブジェクトを追加できます。IBase 型にすると、X 型のオブジェクトを追加できます (どちらも許可されません)。

もちろん、両方の型を持ち、両方の制約を持つ独自のジェネリック コレクション クラスを作成することもできます。しかし、考えられるすべてのコレクション型に対してこれを行う必要はありません。また、すべての機能を複製するには多大な労力が必要です (メソッド呼び出しを含まれているクラスに転送するだけでも)。

また、Base と IBase の両方から派生する BaseWithIBase クラスを作成し、それをコレクション タイプとして使用することもできますが、必要がなければ別の抽象化を強制したくありません。

これを実行時チェックにしたくないので、ツリーをたどって例外をスローすることは受け入れられません。

誰でもこの問題に対するより良いアプローチを提案できますか?

注: Base と IBase は関連がなく、どちらも異なるタイプの基本アイテムであることを指摘するだけです。

編集:

誰もが「そんなことする必要はない」「OOP ではない」と主張したいようです。真実と違うことがあってはならない。この種の質問やコメントを防ぐために、質問から特定のものを削除しようとしていたので、私の実際の状況を含めます。

このコードは、.NET Framework の ServiceProcess.ServiceBase クラスに基づく Windows サービス フレームワークの実装です。この上に独自のフレームワークを追加しています。これは、依存性注入に大きく基づいており、高度にテスト可能であることを目的としています。

コレクションには、ServiceBase と IService の両方から派生したオブジェクトが含まれている必要があります。IService は、コード内およびテスト用に使用される私のフレームワーク拡張機能です。基本的にはこれだけです:

public interface IService 
{
    void Start();
    void Stop();
}

さらに、他にもいくつかのインターフェースがあります。

public interface IRestartableService
{
    void Restart();
}

public interface IConfigurableService
{
    void Configure();
}

etc.. etc.. サービスは次のようになります。

public class MyService : ServiceBase, IService, IConfigurableService {}

私のコードには IService が必要で、Windows には ServiceBase が必要です。したがって、私は IService を使用し、Windows は ServiceBase を使用するため、両方が必要です。IService のみが必要です。その他のインターフェイスはオプションです。

4

4 に答える 4

8

独自のラッパーコレクションを簡単に作成できます。

// TODO: Work out which collection interfaces you want to implement
public class BaseList
{
    // Or use List<IBase>, if that's how you'll be using it more often.
    private List<Base> list = new List<Base>();

    public void Add<T>(T item) where T : Base, IBase
    {
        list.Add(item);
    }
}

両方の制約でジェネリック メソッドを使用することによりAdd、適切な型引数でのみ呼び出すことができることを確認できます。

データを公開する 2 つの方法を使用できます。1 つは(を使用して)IEnumerable<T>返され、もう 1 つは ... を返します。これにより、いずれかの型でLINQ を使用できますが、両方を同時に使用することはできません。IEnumerable<IBase>Cast<T>IEnumerable<Base>

ただし、他の場所ではこれが厄介だと思うかもしれません-通常は必要のない汎用メソッドをコードに散らかしていることに気付くかもしれません。クラス パーツとインターフェイス パーツの両方が必要な場合は十分な理由があるかもしれませんが、一歩下がって、両方が本当に必要かどうかを検討する価値があります。たとえば、クラスの制約をなくすために、インターフェイスに追加できるものはありますか?

于 2013-06-11T05:45:44.827 に答える
0

設計自体が C#/.NET で実装されている OOP に実際には適合しないため、質問に対する適切な答えはありません。

各要素が2つの独立したインターフェースを静的に提供するコレクションが絶対に必要な場合は、ラッパーコレクションまたはラッパークラスのいずれかがWrapper<TFirst, TSecond, T> : IBoth<TFirst, TSecond>問題を解決します。

例:

public interface IBoth<TFirst, TSecond> {
    TFirst AsFirst();
    TSecond AsSecond();
}

public class Wrapper<T, TFirst, TSecond> : IBoth<TFirst, TSecond>
   where T : TFirst, TSecond
{
    private readonly T _value;

    public Wrapper(T value) {
        _value = value;
    }

    public TFirst AsFirst() {
        return _value;
    }

    public TSecond AsSecond() {
        return _value;
    }
}

ただし、本当の問題は、なぜそれが必要なのかということです。標準の OOP モデルが完璧であるとは言いませんが、元の設計上の決定を確認すれば、問題をより簡単に解決できることがよくあります。

于 2013-06-11T05:55:26.280 に答える
0

もう 1 つのオプションはServiceBase、ほとんどのコードを完全に無視し、ServiceBaseAdapterインターフェイス フレンドリではないコードとの通信用に を作成することです。このようなアダプターは、そのメソッドが呼び出されたときにインターフェイス メソッドを呼び出すだけです。

于 2013-06-11T23:07:29.950 に答える
-2

次のようなことを試してください:

List<object> collection = new List<object>();
foreach(var obj in collection.OfType<Base>().OfType<IBase>())
{
// Do what ever you want
}
于 2013-06-11T05:51:15.927 に答える