Question

Le problème:

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

En supposant que le IList sera retourné en lecture seule, je sais que ce que je suis en train de réaliser est sûr (ou est-ce pas?). Est-il possible que je peux accomplir ce que je suis en train? Je ne voudrais pas essayer de me mettre en oeuvre l'algorithme de TasksChain (encore une fois!), Car il serait sujette aux erreurs et conduirait à la duplication de code. Peut-être que je pourrais définir une chaîne abstraite, puis mettre en œuvre à la fois TasksChain et StatesChain à partir de là? Ou peut-être la mise en œuvre d'une classe Chain<T>?

Comment aborderez-vous cette situation?

Détails: J'ai défini une interface ITask:

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

et une interface de IState qui hérite de ITask:

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

J'ai également défini une interface IHasTasksList:

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

et un IHasStatesList:

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

Maintenant, j'ai défini un TasksChain, qui est une classe qui a une certaine logique de code qui manipulera une chaîne de tâches (méfiez-vous que TasksChain lui-même est une sorte de ITask!):

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

    ...

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

    ...
}

Je suis une State la mise en œuvre de manière suivante:

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

qui, comme vous pouvez le voir, utilise des implémentations d'interface explicite à FailureTask « cacher » et montrer à la place de la propriété FailureState.

Le problème vient du fait que je veux aussi définir un StatesChain, qui hérite à la fois de IState et IHasStateList (et que imples également ITask et IHasTaskList, mis en œuvre comme interfaces explicites) et je veux aussi le IHasTaskList de cacher Tasks et seulement montrer la IHasStateList de States. (ce qui est contenu dans « Le problème » section devrait vraiment être après cela, mais je pensais Puting ce premier serait beaucoup plus facile à lire).

(texte pff..long) Merci!

Était-ce utile?

La solution

Sur la ligne où vous obtenez une erreur, vous essayez de revenir IList<IStates> comme si elle était une instance de type IList<ITask>. Cela ne fonctionne pas automtaically, parce que les deux types sont différents (peu importe que les paramètres génériques sont liés).

En C # 3.0 ou plus, il n'y a pas moyen d'y parvenir automatiquement. C # 4.0 ajoute le support de covariance et contravariance, qui sert exactement cet effet. Mais comme vous l'avez dit, cela ne fonctionne que lorsque la collection retournée est en lecture seule. Le type de IList<T> ne garantit pas que, de sorte qu'il ne soit pas comme annotées covariant dans .NET 4.0.

Pour faire ce travail en utilisant C # 4.0, vous aurez besoin d'utiliser vraiment en lecture seule de type , qui a annotation covariant dans le cadre - la meilleure option dans votre cas est IEnumerable<T> (bien que vous pourriez définir votre propre utilisation du modificateur de out T).

Pour ajouter plus de détails, en C # 4.0, vous pouvez déclarer une interface soit comme covariant ou contre-variante. Le premier moyen de cas que le compilateur vous permettra d'effectuer la conversion dont vous avez besoin dans votre exemple (l'autre cas est utile pour les classes d'écriture seulement). Cela se fait par l'ajout d'annotations explicites à la déclaration d'interface (ceux-ci sont déjà disponibles pour .NET 4.0 types). Par exemple, la déclaration de IEnumerable<T> a le sens de l'annotation out qu'il prend en charge covariance:

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

Maintenant, le compilateur vous permettra d'écrire:

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

Autres conseils

En bref, non, ce n'est pas sûr, car une « lecture seule » IList<> n'existe pas (contrat-sage). Il est seulement la mise en œuvre qui rejettera les entrées, mais trop tard, que l'appel lui-même nécessiterait le paramètre de type d'interface à la fois coopération et contravariant en même temps.

Vous pouvez cependant revenir un IEnumerable<> à la place, qui est covariant en C # 4. Comme il suffit d'utiliser LINQ, qui ne devrait pas être trop d'un inconvénient et exprime la nature en lecture seule mieux.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top