Impossible de convertir un IEnumerable <T> en ICollection <T>

91

J'ai défini ce qui suit:

public ICollection<Item> Items { get; set; }

Quand j'exécute ce code:

Items = _item.Get("001");

Je reçois le message suivant:

Error   3   
Cannot implicitly convert type 
'System.Collections.Generic.IEnumerable<Storage.Models.Item>' to 
'System.Collections.Generic.ICollection<Storage.Models.Item>'. 
An explicit conversion exists (are you missing a cast?)

Quelqu'un peut-il expliquer ce que je fais mal. Je suis très confus quant à la différence entre Enumerable, Collections et en utilisant ToList ()

Informations ajoutées

Plus tard dans mon code, j'ai ce qui suit:

for (var index = 0; index < Items.Count(); index++) 

Est-ce que je serais d'accord pour définir des éléments comme un IEnumerable?

Samantha JT Star
la source
3
Pouvez-vous fournir plus d'informations sur le type de _item et la signature de Get (string) (spécifiquement le type de retour)?
Dr Andrew Burnett-Thompson
Pourquoi ne pas changer le type comme ça? public IEnumerable<Item> Items { get; set; }Avez-vous une raison particulière de l'avoir comme ICollection?
Shadow Wizard est une oreille pour vous le
IEnumerable <T> Get (chaîne pk);
Samantha JT Star

Réponses:

113

ICollection<T>hérite de IEnumerable<T>so pour attribuer le résultat de

IEnumerable<T> Get(string pk)

à un ICollection<T>il y a deux façons.

// 1. You know that the referenced object implements `ICollection<T>`,
//    so you can use a cast
ICollection<T> c = (ICollection<T>)Get("pk");

// 2. The returned object can be any `IEnumerable<T>`, so you need to 
//    enumerate it and put it into something implementing `ICollection<T>`. 
//    The easiest is to use `ToList()`:
ICollection<T> c = Get("pk").ToList();

La deuxième option est plus flexible, mais a un impact beaucoup plus important sur les performances. Une autre option consiste à stocker le résultat sous forme de, IEnumerable<T>sauf si vous avez besoin des fonctionnalités supplémentaires ajoutées par l' ICollection<T>interface.

Commentaire supplémentaire sur les performances

La boucle que vous avez

for (var index = 0; index < Items.Count(); index++)

fonctionne sur un IEnumerable<T>mais il est inefficace; chaque appel à Count()nécessite une énumération complète de tous les éléments. Utilisez une collection et la Countpropriété (sans les parenthèses) ou convertissez-la en boucle foreach:

foreach(var item in Items)
Anders Abel
la source
1
Quelle est la fonctionnalité supplémentaire ajoutée par ICollection?
Samantha JT Star
7
ICollection<T>peut être manipulé (voir le doc pour plus de détails), tandis qu'un IEnumerable<T>ne peut être énuméré.
Anders Abel
J'ai besoin de quelque chose où je peux exécuter différentes requêtes LINQ. Je n'ai pas besoin d'ajouter ou de faire quoi que ce soit de ce genre à la liste. Cela signifie-t-il que je serais mieux avec ICollection.
Samantha JT Star
LINQ fonctionne IEnumerable<T>donc IEnumerable<T>suffit dans ce cas.
Anders Abel
31

Vous ne pouvez pas convertir directement de IEnumerable<T>à ICollection<T>. Vous pouvez utiliser la ToListméthode de IEnumerable<T>pour le convertir enICollection<T>

someICollection = SomeIEnumerable.ToList();

Haris Hasan
la source
Cela fonctionne pour moi. Mais est-ce une bonne idée pour moi de convertir ou est-ce que je serais préférable d'utiliser IEnumerable comme type pour les éléments. Si j'utilise IEnumerable, est-ce que je le fais pour que je puisse faire plus d'actions sur les éléments si nécessaire plus tard dans mon programme?
Samantha JT Star
ICollection implémente déjà IEnumerable. Si vous êtes préoccupé par les actions disponibles, je pense que vous devriez opter pour ICollection. Il est préférable que vous puissiez lire la différence réelle entre les deux et ensuite décider de ce que vous voulez réellement utiliser
Haris Hasan
J'essaye de comprendre quels avantages j'aurais si j'utilisais ToList (). Une fois que j'ai les données, je souhaite exécuter des requêtes LINQ sur des éléments.
Samantha JT Star
ToList () fonctionnera mais comme vous l'avez indiqué dans vos commentaires, Get () renvoie une ICollection, il vous suffit donc de faire un cast en ICollection <T>, ou mieux encore, de changer la signature de retour de Get en ICollection <T> . L'utilisation de ToList ou ToArray fonctionnera, mais cela entraînera également une opération inutile de création de mémoire / copie
Dr.Andrew Burnett-Thompson
Vous pouvez exécuter des requêtes LINQ sur IEnumerable et ICollection. Avantages .. celui que je connais en ce moment est le Count ICollection maintient le compte et IEnumerable renvoie le résultat en calculant le nombre d'éléments dont il dispose. ICollection fournit une méthode de suppression supplémentaire que Ienumerables ne fait pas
Haris Hasan
1

En attendant plus d'informations sur la question:

veuillez fournir plus d'informations sur le type d'article et la signature de Get

Deux choses que vous pouvez essayer sont:

  • Pour convertir la valeur de retour de _item, accédez à (ICollection)
  • deuxièmement, utiliser _item.Get ("001"). ToArray () ou _item.Get ("001"). ToList ()

Veuillez noter que le second entraînera un impact sur les performances de la copie de la baie. Si la signature (type de retour) de Get n'est pas une ICollection, la première ne fonctionnera pas, si elle n'est pas IEnumerable, la seconde ne fonctionnera pas.


Suite à votre clarification à la question et dans les commentaires, je déclarerais personnellement le type de retour de _item.Get ("001") à ICollection. Cela signifie que vous n'aurez pas à faire de casting ou de conversion (via ToList / ToArray) qui impliquerait une opération de création / copie inutile.

// Leave this the same
public ICollection<Item> Items { get; set; }

// Change function signature here:
// As you mention Item uses the same underlying type, just return an ICollection<T>
public ICollection<Item> Get(string value); 

// Ideally here you want to call .Count on the collectoin, not .Count() on 
// IEnumerable, as this will result in a new Enumerator being created 
// per loop iteration
for (var index = 0; index < Items.Count(); index++) 

Meilleures salutations,

Dr Andrew Burnett-Thompson
la source
1
La signature est: IEnumerable <T> Get (string pk); Items est utilisé pour contenir les enregistrements Item renvoyés. Plus tard, je les parcoure pour créer un rapport. Je serais d'accord pour utiliser tout ce qui fonctionne. Selon vous, quel serait le meilleur type de type de données?
Samantha JT Star
1
Je suggérerais de changer le type de données des éléments en IEnumerable <T> pour correspondre à Get, ou de changer le type de retour de Get en ICollection <T> pour correspondre aux éléments. Je suppose que ces deux sont le même type sous-jacent juste déclaré différemment? S'ils sont du même type, vous voulez éviter d'utiliser ToList () ou ToArray () car cela effectue une allocation de mémoire et une copie de tableau inutiles. S'ils ne sont pas du même type, vous devez faire une conversion
Dr Andrew Burnett-Thompson
J'ai ajouté à la question pour montrer où j'utilise plus tard les éléments. Vous avez raison de dire le même type sous-jacent.
Samantha JT Star
Ok, alors je changerais simplement le type de retour de Get, car alors vous n'avez pas besoin de faire de conversion (via ToList / ToArray, qui est lent), ni de casting. J'espère que cela aide :)
Dr Andrew Burnett-Thompson