web-dev-qa-db-fra.com

En C #, est-il possible de convertir une liste <Child> en Liste <Parent>?

Je veux faire quelque chose comme ça:

List<Child> childList = new List<Child>();
...
List<Parent> parentList = childList;

Cependant, parce que parentList est un Liste de l'ancêtre de l'enfant, plutôt qu'un ancêtre direct, je suis incapable de le faire. Existe-t-il une solution de contournement (autre que l'ajout de chaque élément individuellement)?

45
Matthew

Le casting direct n'est pas autorisé car il n'y a aucun moyen de le rendre dactylographié. Si vous avez une liste de girafes et que vous la définissez sur une liste d'animaux, vous pouvez alors ajouter un tigre à une liste de girafes! Le compilateur ne vous arrêterait pas, car bien sûr, un tigre peut entrer dans une liste d'animaux. Le compilateur ne peut vous arrêter que lors de la conversion non sécurisée.

En C # 4, nous allons prendre en charge la covariance et la contravariance des interfaces SAFE et des types de délégués paramétrés avec des types de référence. Voir ici pour plus de détails:

http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx

57
Eric Lippert

Utiliser LINQ:

List<Parent> parentList = childList.Cast<Parent>().ToList();

Documentation pour Cast<>()

62
recursive

En 2009, Eric nous a dit que les choses changeraient en C # 4. Où en sommes-nous aujourd'hui?

Les classes utilisées dans ma réponse se trouvent au bas de la page. Pour faciliter la tâche, nous utiliserons une classe Mammal comme "parent" et les classes Cat et Dog comme des "enfants". Les chats et les chiens sont deux mammifères. , mais un chat n’est pas un chien et un chien n’est pas un chat.

Ce n'est toujours pas légal, et ne peut pas être:

List<Cat> cats = new List<Cat>();

List<Mammal> mammals = cats;

Pourquoi pas? Les chats sont des mammifères, alors pourquoi ne pouvons-nous pas attribuer une liste de chats à un List<Mammal>?

Parce que si nous étions autorisés à stocker une référence à un List<Cat> dans une variable List<Mammal>, nous serions alors en mesure de compiler le code suivant pour ajouter un chien à une liste de chats:

mammals.Add(new Dog());

Nous ne devons pas permettre cela! Rappelez-vous, mammals est simplement une référence à cats. Dog ne descend pas de Cat et n'a pas d'activité dans une liste d'objets Cat.

À partir de .NET Framework 4 , plusieurs interfaces génériques ont des paramètres de type covariant déclarés avec le mot clé out Generic Modifier introduit en C # 4. Parmi ces interfaces, on trouve IEnumerable<T> qui est bien sûr implémenté par List<T> .

Cela signifie que nous can allons maintenant convertir un List<Cat> en IEnumerable<Mammal>:

IEnumerable<Mammal> mammalsEnumerable = cats;

Nous ne pouvons pas ajouter un nouveau Dog à mammalsEnumerable car IEnumerable<out T> est une interface "en lecture seule", c’est-à-dire qu’il n’a pas de méthode Add(), mais nous peut utilisons maintenant cats partout où un IEnumerable<Mammal> peut être utilisé. Par exemple, nous pouvons concaténer mammalsEnumerable avec un List<Dog> pour renvoyer une nouvelle séquence:

void Main()
{
    List<Cat> cats = new List<Cat> { new Cat() };
    IEnumerable<Mammal> mammalsEnumerable =
        AddDogs(cats); // AddDogs() takes an IEnumerable<Mammal>
    Console.WriteLine(mammalsEnumerable.Count()); // Output: 3. One cat, two dogs.
}

public IEnumerable<Mammal> AddDogs(IEnumerable<Mammal> parentSequence)
{
    List<Dog> dogs = new List<Dog> { new Dog(), new Dog() };
    return parentSequence.Concat(dogs);
}

Définitions de classe:

public abstract class Mammal { }

public class Cat: Mammal { }

public class Dog : Mammal { }
3
Stephen Kennedy

oui, tu peux le faire comme

var result = List.And(x => x.Parent.All(b => b.ParentId == value));
0
caras