Pregunta

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

Suponiendo que el IList regresó, sé que lo que estoy tratando de lograr es seguro va a ser de sólo lectura (o no es?). ¿Hay alguna manera de lograr lo que estoy tratando? No me gustaría tratar de implementar el algoritmo mismo TasksChain (otra vez!), Ya que sería propenso a errores y llevaría a la duplicación de código. Tal vez sólo podía definir una cadena abstracto y luego implementar tanto TasksChain y StatesChain a partir de ahí? O tal vez la implementación de una clase Chain<T>?

¿Cómo acercarse a esta situación?

Los detalles: He definido una interfaz ITask:

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

y una interfaz IState que hereda de ITask:

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

También he definido un interfaz IHasTasksList:

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

y un IHasStatesList:

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

Ahora, he definido un TasksChain, que es una clase que tiene cierta lógica código que manipular una cadena de tareas (ten en cuenta que TasksChain es en sí misma una especie de ITask!):

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

    ...

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

    ...
}

Me estoy poniendo en práctica un State la siguiente manera:

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

que, como se puede ver, hace uso de implementaciones de interfaz explícita a FailureTask "ocultar" y en lugar de mostrar la propiedad FailureState.

El problema viene del hecho de que también quiero definir un StatesChain, que hereda tanto desde IState y IHasStateList (y que también imples ITask y IHasTaskList, implementado como interfaces explícitas) y yo quiero que también IHasTaskList piel de Tasks y sólo mostrar IHasStateList de States. (lo que está contenido en la sección "El problema" en realidad debería ser después de esto, pero pensé puting primera sería mucho más fácil de leer).

(texto pff..long) Gracias!

¿Fue útil?

Solución

En la línea donde se produce un error, que está tratando de volver IList<IStates> como si fuera una instancia de tipo IList<ITask>. Esto no funciona automtaically, debido a que los dos tipos son diferentes (no importa que los parámetros genéricos están relacionados).

En C # 3.0 o mayor, no hay manera de conseguir que de forma automática. C # 4.0 añade soporte para covarianza y contravarianza, que sirve precisamente para este propósito. Pero como usted señaló, esto sólo funciona cuando la colección devuelta es de sólo lectura. El tipo IList<T> no garantiza que, por lo que no está anotado como covariante en .NET 4.0.

Para hacer este trabajo usando C # 4.0, tendrá que utilizar realmente sólo lectura escribir , que tiene anotación covariante en el marco - la mejor opción en su caso es IEnumerable<T> (aunque se podría definir su propio utilizando el modificador out T).

Para añadir más detalles, en C # 4.0, se puede declarar una interfaz, ya sea como covariante o el contra-variante. El primer medio de casos que el compilador le permitirá realizar la conversión se necesitaba en su ejemplo (el otro caso es útil para las clases de sólo escritura). Esto se hace mediante la adición de anotaciones explícitas a la declaración de interfaz (estos ya están disponibles para .NET 4.0 tipos). Por ejemplo, la declaración de IEnumerable<T> tiene el significado out anotación que apoya covarianza:

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

Ahora, el compilador le permitirá escribir:

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

Otros consejos

En resumen, no, no es seguro, ya que una de "sólo lectura" IList<> no existe (contrato-wise). Es sólo la aplicación que rechaza las entradas, pero que es demasiado tarde, ya que la llamada en sí requeriría el parámetro de tipo de interfaz a ser a la vez co- y contravariant al mismo tiempo.

Se podría, sin embargo, regresar un IEnumerable<> lugar, que es covariante en C # 4. Dado que esto es suficiente para utilizar LINQ, que no debe ser demasiado de un inconveniente y expresa la naturaleza de sólo lectura mejor.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top