Diffuser la liste <T> vers la liste <Interface>

110
public interface IDic
{
    int Id { get; set; }
    string Name { get; set; }
}
public class Client : IDic
{

}

Comment puis - je jeter List<Client>à List<IDic>?

Andrew Kalashnikov
la source

Réponses:

250

Vous ne pouvez pas le lancer (en préservant l'identité de référence) - ce serait dangereux. Par exemple:

public interface IFruit {}

public class Apple : IFruit {}
public class Banana : IFruit {}

...

List<Apple> apples = new List<Apple>();
List<IFruit> fruit = apples; // Fortunately not allowed
fruit.Add(new Banana());

// Eek - it's a banana!
Apple apple = apples[0];

Vous pouvez maintenant convertir un List<Apple>en un IEnumerable<IFruit>dans .NET 4 / C # 4 en raison de la covariance, mais si vous voulez un, List<IFruit>vous devez créer une nouvelle liste. Par exemple:

// In .NET 4, using the covariance of IEnumerable<T>
List<IFruit> fruit = apples.ToList<IFruit>();

// In .NET 3.5
List<IFruit> fruit = apples.Cast<IFruit>().ToList();

Mais ce n'est pas la même chose que de lancer la liste d'origine - car il existe maintenant deux listes distinctes . C'est sûr, mais vous devez comprendre que les modifications apportées à une liste ne seront pas vues dans l'autre liste. (Les modifications apportées aux objets auxquels les listes se réfèrent seront bien sûr visibles.)

Jon Skeet
la source
6
comme s'ils étaient «toujours dans le bâtiment»!
Andras Zoltan
1
Quelle est la différence entre faire la tolérance de covariance et faire une nouvelle liste <IFruit> (); puis un foreach sur la liste d'origine et l'ajout de chaque élément à la liste IFruit? Dans le sens foreach ... la référence de l'objet serait la même, n'est-ce pas? Donc ... si c'est vrai, cela n'a guère de sens pour moi personnellement que vous ne puissiez pas simplement jeter directement toute la liste. ?
Robert Noack
3
@RobertNoack: Qu'entendez-vous par "la référence d'objet"? La référence d'objet de chaque élément est la même, mais ce n'est pas la même chose que la conversion de la référence de liste elle-même. Supposons que vous puissiez transtyper la référence, de sorte que vous ayez une référence de type à la compilation List<IFruit>qui était en fait une référence à un fichier List<Apple>. À quoi vous attendriez-vous si vous ajoutiez une Bananaréférence à cela List<IFruit>?
Jon Skeet
1
Oh. Je pense que j'ai besoin d'apprendre à lire. Je pensais que la réponse originale disait que les objets de la liste seraient profondément copiés et recréés, ce qui n'avait aucun sens pour moi. Mais j'ai clairement manqué la partie où seule une nouvelle liste est créée avec les mêmes objets.
Robert Noack
2
@ TrươngQuốcKhánh: Je n'ai aucune idée de ce à quoi ressemble votre code, ou si vous avez une usingdirective pour System.Linq, ou pourquoi vous essayez de l'appeler, je ne sais pas comment vous vous attendez à ce que je puisse vous aider. Je vous suggère de faire plus de recherches, et si vous êtes toujours bloqué, vous posez une question avec un exemple reproductible minimal .
Jon Skeet
6

Un itérateur Cast et .ToList ():

List<IDic> casted = input.Cast<IDic>().ToList() fera l'affaire.

À l'origine, j'ai dit que la covariance fonctionnerait - mais comme Jon l'a souligné à juste titre; non ce ne sera pas!

Et à l'origine, j'ai aussi bêtement arrêté l' ToList()appel

Andras Zoltan
la source
Castrenvoie une IEnumerable<T>, pas une List<T>- et non, la covariance ne permettra pas cette conversion, car elle serait dangereuse - voir ma réponse.
Jon Skeet
De la page à laquelle vous avez lié: "Seuls les types d'interface et les types de délégués peuvent avoir des paramètres de type de variante"
Jon Skeet
@Jon - J'avais réalisé qu'il ToList()manquait avant de lire votre commentaire; mais oui, comme vous l'avez montré bien sûr, la Covariance ne fonctionnera pas! Doh!
Andras Zoltan
Droite. La covariance peut toujours aider, car cela signifie que vous n'avez pas besoin de l' Castappel dans .NET 4, tant que vous spécifiez l'argument de type à ToList.
Jon Skeet
5

J'ai aussi eu ce problème et après avoir lu la réponse de Jon Skeet, j'ai modifié mon code de l'utilisation List<T>à l'utilisation IEnumerable<T>. Bien que cela ne répond pas à la question de l' origine de l'OP Comment puis - je jeter List<Client>àList<IDic> , il n'évite la nécessité de le faire et peut donc être utile pour les autres qui rencontrent ce problème. Cela suppose bien sûr que le code qui nécessite l'utilisation de List<IDic>est sous votre contrôle.

Par exemple:

public void ProcessIDic(IEnumerable<IDic> sequence)
{
   // Implementation
}

Au lieu de:

public void ProcessIDic(List<IDic> list)
{
   // Implementation
}
Ɖ diamant ǤeezeƦ
la source
3

Si vous pouvez utiliser LINQ, vous pouvez le faire ...

List<Client> clientList = new List<Client>();
List<IDic> list = clientList.Select(c => (IDic)c).ToList();
musefan
la source
3
List<Client> listOfA = new List<Client>();
List<IDic> list = listOfA.Cast<IDic>().ToList();
Marc Messing
la source
0

Ce n'est possible qu'en créant de nouveaux List<IDic>et en transférant tous les éléments.

Piotr Auguscik
la source
Un commentaire pourquoi a-t-il été rejeté, puisque le sens général est le même que toutes les autres réponses?
Piotr Auguscik
Je suppose que vous avez été rejeté parce que vous avez dit qu'il était seulement possible de créer une nouvelle liste, mais d'autres ont publié le contraire ... pas mon
vote négatif
Eh bien, ils créent de nouvelles listes mais pas avec un nouvel opérateur, ce qui ne change pas le fait qu'ils le font.
Piotr Auguscik
0

Dans .Net 3.5, vous pouvez effectuer les opérations suivantes:

List<ISomeInterface> interfaceList = new List<ISomeInterface>(list.Cast<ISomeInterface>());

Le constructeur de List dans ce cas prend un IEnumerable.
list n'est cependant convertible qu'en IEnumerable. Même si myObj peut être convertible en ISomeInterface, le type IEnumerable n'est pas convertible en IEnumerable.

Kent Aguilar
la source
Le problème est que cela fait une copie de la liste, la plupart du temps, vous souhaitez effectuer des opérations sur la liste d'origine
lance le