2

問題:

class StatesChain : IState, IHasStateList {
    private TasksChain tasks = new TasksChain();

     ...

    public IList<IState> States {
        get { return _taskChain.Tasks; }
    }

    IList<ITask> IHasTasksCollection.Tasks {
        get { return _taskChain.Tasks; } <-- ERROR! You can't do this in C#!
                                             I want to return an IList<ITask> from
                                             an IList<IStates>.
    }
}

返されたものが読み取り専用であると仮定するとIList、私が達成しようとしていることは安全であることがわかります(またはそうではありませんか?)。私がしようとしていることを達成する方法はありますか?TasksChainエラーが発生しやすく、コードの重複につながるため、アルゴリズムを自分で実装しようとはしません(もう一度!)。たぶん、抽象的なチェーンを定義して、そこから両方TasksChainを実装することができますStatesChainか?または多分Chain<T>クラスを実装しますか?

この状況にどのようにアプローチしますか?

詳細:ITaskインターフェース を定義しました:

public interface ITask {
    bool Run();
    ITask FailureTask { get; }
}

およびIStateから継承するインターフェイスITask

public interface IState : ITask {
    IState FailureState { get; }
}

IHasTasksListインターフェイスも定義しました。

interface IHasTasksList {
    List<Tasks> Tasks { get; }
}

IHasStatesList

interface IHasTasksList {
    List<Tasks> States { get; }
}

ここで、を定義しましたTasksChain。これは、一連のタスクを操作するコードロジックを持つクラスです(TasksChainそれ自体が一種のITask!であることに注意してください)。

class TasksChain : ITask, IHasTasksList {
    IList<ITask> tasks = new List<ITask>();

    ...

    public List<ITask> Tasks { get { return _tasks; } }

    ...
}

私はState次の方法を実装しています:

public class State : IState {
    private readonly TaskChain _taskChain = new TaskChain();

    public State(Precondition precondition, Execution execution) {
        _taskChain.Tasks.Add(precondition);
        _taskChain.Tasks.Add(execution);
    }

    public bool Run() {
        return _taskChain.Run();
    }

    public IState FailureState {
        get { return (IState)_taskChain.Tasks[0].FailureTask; }
    }

    ITask ITask.FailureTask {
        get { return FailureState; }
    }
}

ご覧のとおり、これは明示的なインターフェイス実装を利用して「非表示」FailureTaskにし、代わりにFailureStateプロパティを表示します。

問題は、とのStatesChain両方を継承する(および、明示的なインターフェイスとして実装されたとを実装する)も定義したいという事実に起因します。また、を非表示にして、を表示するだけにします。(「問題」のセクションに含まれているのは、実際にはこの後のはずですが、最初に置く方がはるかに読みやすいと思いました)。IStateIHasStateListITaskIHasTaskListIHasTaskListTasksIHasStateListStates

(pff..long text)ありがとう!

4

2 に答える 2

2

つまり、「読み取り専用」IList<>が存在しないため(契約上)、安全ではありません。エントリを拒否するのは実装だけですが、呼び出し自体がインターフェイスタイプパラメータを同時に共変と反変の両方にする必要があるため、遅すぎます。

ただし、IEnumerable<>代わりに、C#4で共変であるを返すこともできます。これはLINQを使用するのに十分なので、それほど大きな欠点ではなく、読み取り専用の性質をより適切に表現します。

于 2010-04-26T23:38:22.477 に答える
1

IList<IStates>エラーが発生した行では、タイプのインスタンスであるかのように戻ろうとしていますIList<ITask>。2つのタイプが異なるため、これは自動的には機能しません(汎用パラメーターが関連している場合でも)。

C#3.0以前では、これを自動的に実現する方法はありません。C#4.0は、共変性と反変性のサポートを追加します。これはまさにこの目的に役立ちます。ただし、ご指摘のとおり、これは、返されたコレクションが読み取り専用の場合にのみ機能します。このIList<T>型はそれを保証するものではないため、.NET4.0では共変として注釈が付けられていません。

C#4.0を使用してこれを機能させるには、フレームワークに共変アノテーションを持つ真の読み取り専用タイプを使用する必要があります。この場合の最適なオプションは(修飾子IEnumerable<T>を使用して独自に定義することもできます)です。out T

詳細を追加するために、C#4.0では、インターフェイスを共変または反変として宣言できます。最初のケースは、コンパイラーが例で必要な変換を実行できるようにすることを意味します(他のケースは書き込み専用クラスに役立ちます)。これは、インターフェイス宣言に明示的な注釈を追加することによって行われます(これらは.NET 4.0タイプですでに使用可能です)。たとえば、の宣言にIEnumerable<T>は、out共分散をサポートすることを意味する注釈があります。

public interface IEnumerable<out T> : IEnumerable { /* ... */ }

これで、コンパイラーは次のように記述できるようになります。

IEnumerable<IState> states = ...
IEnumerable<ITask> tasks = states;
于 2010-04-26T23:39:07.973 に答える