2010-03-10 2 views
38

Considérons ce bit de code obfusqué. L'intention est de créer un nouvel objet à la volée via le constructeur anonyme et yield return. L'objectif est d'éviter d'avoir à maintenir une collection locale simplement pour le return.C#: retour de rendement dans un échec foreach - le corps ne peut pas être un bloc d'itérateur

public static List<DesktopComputer> BuildComputerAssets() 
{   
    List<string> idTags = GetComputerIdTags(); 

    foreach (var pcTag in idTags) 
    { 
     yield return new DesktopComputer() {AssetTag= pcTag 
              , Description = "PC " + pcTag 
              , AcquireDate = DateTime.Now 
              }; 
    }    
} 

Malheureusement, ce morceau de code produit une exception:

Erreur 28 Le corps de 'Foo.BuildComputerAssets() ne peut pas être un bloc itérateur parce que 'System.Collections.Generic.List' n'est pas un type d'interface iterator

questions

  • Que signifie ce message d'erreur? Comment éviter cette erreur et utiliser correctement yield return?

Répondre

49

Vous ne pouvez utiliser yield return dans une fonction qui renvoie un IEnumerable ou un IEnumerator, pas List<T>. Vous devez changer votre fonction pour retourner un IEnumerable<DesktopComputer>.

Vous pouvez réécrire la fonction à utiliser List<T>.ConvertAll:

return GetComputerIdTags().ConvertAll(pcTag => 
    new DesktopComputer() { 
     AssetTag = pcTag, 
     Description = "PC " + pcTag, 
     AcquireDate = DateTime.Now 
    }); 
16

Votre signature de la méthode est mauvaise. Il devrait être:

public static IEnumerable<DesktopComputer> BuildComputerAssets() 
8

yield ne fonctionne que sur les types Iterator:

La déclaration de rendement ne peut apparaître à l'intérieur d'un bloc itérateur

Iterators sont définis comme

Le type de retour d'un itérateur doit être IEnumerable, IEnumerator, IEnume rable <T>, ou IEnumeratorT >.

IList et IList <T> ne mettre en œuvre IEnumerable/IEnumerable <T>, mais chaque appelant à un recenseur attend l'un des quatre types ci-dessus et personne d'autre.

2

Vous pouvez également implémenter la même fonctionnalité à l'aide d'une requête LINQ (en C# 3.0+). C'est moins efficace que d'utiliser la méthode ConvertAll, mais c'est plus général. Par la suite, vous devrez peut-être utiliser d'autres fonctions telles que le filtrage LINQ:

return (from pcTag in GetComputerIdTags() 
     select new DesktopComputer() { 
      AssetTag = pcTag, 
      Description = "PC " + pcTag, 
      AcquireDate = DateTime.Now 
     }).ToList(); 

La méthode ToList convertit le résultat IEnumerable<T>-List<T>.Personnellement, je n'aime pas ConvertAll, car il fait la même chose que LINQ. Mais comme il a été ajouté plus tôt, il ne peut pas être utilisé avec LINQ (il aurait dû s'appeler Select).

Questions connexes