Domanda

Il problema:

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

Supponendo che il IList restituito sarà di sola lettura, so che quello che sto cercando di realizzare è sicuro (o no?). C'è un modo posso fare quello che sto cercando? Non vorrei cercare di implementare l'algoritmo di me stesso TasksChain (di nuovo!), Come sarebbe soggetto ad errori e porterebbe alla duplicazione del codice. Forse avrei potuto solo definire una catena astratto e quindi implementare sia TasksChain e StatesChain da lì? O forse l'implementazione di una classe Chain<T>?

Come ci si avvicina a questa situazione?

I Dettagli: Ho definito un'interfaccia ITask:

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

e un'interfaccia IState che eredita da ITask:

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

Inoltre ho definito un'interfaccia IHasTasksList:

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

ed un IHasStatesList:

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

Ora, ho definito un TasksChain, che è una classe che ha una certa logica codice che manipolare una catena di attività (attenzione che TasksChain è di per sé una sorta di ITask!):

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

    ...

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

    ...
}

Io sono l'attuazione di un State seguente modo:

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

, che, come si può vedere, si avvale di implementazioni di interfacce esplicite per FailureTask "nascondere" e mostrare invece di proprietà FailureState.

Il problema deriva dal fatto che anche io voglio definire un StatesChain, che eredita sia da IState e IHasStateList (e che imples anche ITask e IHasTaskList, implementati come interfacce esplicite) e voglio che anche IHasTaskList pelle di Tasks e solo mostrare IHasStateList di States. (ciò che è contenuto nella sezione "Il problema" in realtà dovrebbe essere dopo questo, ma ho pensato che mette in primo luogo sarebbe modo più leggibilità).

(testo pff..long) Grazie!

È stato utile?

Soluzione

Nella riga in cui si verifica un errore, si sta cercando di tornare IList<IStates> come se fosse un'istanza di tipo IList<ITask>. Questo non funziona automtaically, perché i due tipi sono diversi (non importa che i parametri generici sono correlate).

In C # 3.0 o più anziani, non c'è modo per raggiungere questo automaticamente. C # 4.0 aggiunge il supporto per covarianza e controvarianza, che serve proprio per questo scopo. Ma come avrete notato, questo funziona solo quando la raccolta restituita è di sola lettura. Il tipo IList<T> non garantisce che, in modo che non è annotato come covariante in .NET 4.0.

Per fare questo lavoro utilizzando C # 4.0, è necessario utilizzare veramente sola lettura di tipo , che ha l'annotazione covariante nel quadro - l'opzione migliore nel tuo caso è IEnumerable<T> (anche se si potrebbe definire il proprio utilizzando il modificatore out T).

Per aggiungere ulteriori dettagli, in C # 4.0, è possibile dichiarare un'interfaccia sia come covariante o contra-variante. Il primo caso significa che il compilatore vi permetterà di eseguire la conversione avevi bisogno nel vostro esempio (l'altro caso è utile per le classi di sola scrittura). Questo viene fatto con l'aggiunta di annotazioni esplicite alla dichiarazione di interfaccia (questi sono già disponibili per .NET 4.0 tipi). Per esempio, la dichiarazione di IEnumerable<T> ha il significato out annotazioni che supporta covarianza:

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

Ora, il compilatore vi permetterà di scrivere:

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

Altri suggerimenti

In breve, non non è sicuro, dal momento che un "read-only" IList<> non esiste (contratto-saggio). E 'solo l'attuazione, che rifiuterà le voci, ma che è troppo tardi, come la chiamata in sé richiederebbe il parametro tipo di interfaccia per essere sia cooperazione e controvariante allo stesso tempo.

Si potrebbe, tuttavia, restituite un IEnumerable<> invece, che è covariante in C # 4. Dal momento che questo è sufficiente per utilizzare LINQ, che non dovrebbe essere troppo di un inconveniente ed esprime la natura di sola lettura migliore.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top