Frage

Das Problem:

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>.
    }
}

Unter der Annahme, die IList zurückgegeben wird nur gelesen werden, ich weiß, dass das, was ich versuche, sicher zu erreichen ist (oder ist es nicht?). Gibt es irgendeine Weise, die ich erreichen kann, was ich versuche? Ich möchte nicht versuchen, mir den TasksChain Algorithmus zu implementieren (wieder!), Da es fehleranfällig sein würde und zu Code-Duplizierung führen würde. Vielleicht könnte ich nur eine abstrakte Kette definieren und dann beide TasksChain und StatesChain von dort implementieren? Oder vielleicht eine Chain<T> Klasse implementieren?

Wie würden Sie diese Situation angehen?

Die Details: Ich habe eine ITask Schnittstelle definiert:

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

und eine IState Schnittstelle, die erbt von ITask:

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

Ich habe auch eine IHasTasksList Schnittstelle definiert:

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

und ein IHasStatesList:

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

Nun, ich habe eine TasksChain definiert, dass eine Klasse, die einige Code-Logik hat, die eine Kette von Aufgaben manipulieren (Beachten Sie, dass TasksChain ist selbst eine Art ITask!):

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

    ...

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

    ...
}

Ich bin ein State die folgende Art und Weise der Umsetzung:

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

, die, wie Sie sehen können, die Verwendung von expliziten Schnittstellenimplementierungen zu „verstecken“ FailureTask macht und stattdessen FailureState Eigenschaft zeigen.

Das Problem kommt von der Tatsache, dass ich möchte auch eine StatesChain definieren, dass erbt sowohl von IState und IHasStateList (und das auch imples ITask und IHasTaskList, als explizite Schnittstellen implementiert) und ich will es auch verstecken IHasTaskList des Tasks und nur zeigen IHasStateList des States. (Was in „Das Problem“ Abschnitt auch wirklich danach sein sollte, aber ich dachte, es wäre Puting erster Weg leserfreundlicher sein).

(pff..long Text) Danke!

War es hilfreich?

Lösung

Auf der Linie, wo Sie eine Fehlermeldung erhalten, Sie versuchen IList<IStates> zurückzukehren, als ob es sich um eine Instanz vom Typ IList<ITask> war. Dies gilt nicht automtaically arbeiten, weil die beiden Arten unterschiedlich sind (egal, dass die generischen Parameter verwandt sind).

In C # 3.0 oder älter ist, gibt es keine Möglichkeit, dass automatisch zu erreichen. C # 4.0 fügt Unterstützung für Kovarianz und Kontra, die genau diesem Zweck dient. Aber wie Sie erwähnt, funktioniert dies nur, wenn die zurückgegebene Auflistung schreibgeschützt ist. Die IList<T> Art garantiert nicht, dass, so ist es nicht kommentiert als covariant in .NET 4.0.

Um diese Arbeit zu machen, mit C # 4.0, die Sie verwenden müssen werden wirklich Nur-Lese-, die kovariante Anmerkung im Rahmen hat - die beste Option in Ihrem Fall ist IEnumerable<T> (obwohl man könnte definieren Sie Ihre eigenen die out T Modifier).

Für weitere Details, in C # 4.0, fügen Sie eine Schnittstelle entweder als kovarianten oder kontravarianten erklären können. Der erste Fall bedeutet, dass der Compiler die Konvertierung Sie in Ihrem Beispiel benötigten ausführen kann (der andere Fall ist geeignet für write-only-Klassen). Dies wird durch Hinzufügen explizite Anmerkungen zu der Interface-Deklaration erfolgt (diese sind bereits verfügbar für .NET 4.0-Typen). Zum Beispiel hat die Erklärung von IEnumerable<T> die out Anmerkung Bedeutung, die es unterstützt Kovarianz:

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

Nun wird die Compiler Sie schreiben lassen:

IEnumerable<IState> states = ...
IEnumerable<ITask> tasks = states;

Andere Tipps

Kurz gesagt, nein ist es nicht sicher, da ein „read-only“ IList<> existiert nicht (Vertrag weist). Es ist nur die Umsetzung, welche Einträge ablehnen, aber das ist zu spät, da der Anruf selbst würde die Interface-Typen Parameter erfordert sowohl Co- und kontra zugleich sein.

Sie könnten jedoch zurückgeben eine IEnumerable<> statt, die in C covariant ist # 4. Da dies genug LINQ zu verwenden, die nicht zu viel von einem Nachteil sein sollte, und drückt die Nur-Lese-Natur besser.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top