2009-11-19 2 views
26

Existe-t-il un moyen de renvoyer éventuellement une valeur nulle avec un itérateur piloté par "return return"?Rendement avec Null

Je voudrais retourner un null dans certains cas et je ne pense pas que cela soit particulier à IEnumerable de type string. Même chose pour IEnumerable de type int, etc. Merci

static void Main(string[] args) 
{ 
    var Items = GetItems(); 

    if (Items != null) 
    { 
     foreach (var item in Items) 
     { 
      Console.WriteLine(item); 
     } 
    } 
    else 
    { 
     Console.WriteLine("<null>"); 
    } 
} 

static IEnumerable<string> GetItems() 
{ 
    if (false) 
    { 
     yield return "Andy"; 
     yield return "Jennifer"; 
    } 

    return null; // <- Compiler Error: 
    // Cannot return a value from an iterator. 
    // Use the yield return statement to return a value, 
    // or yield break to end the iteration. 
} 

Répondre

34

Si vous avez besoin à des choses comme que (ou lancer des choses comme ArgumentException immédiatement), vous devez séparer votre iterator en deux méthodes:

public IEnumerable<string> GetItems() { 
    if (something) return null; 
    return GetItemsInternal(); 
} 

private IEnumerable<string> GetItemsInternal() { 
    // the actual iterator with "yield return" goes here. 
} 
+1

la réponse à ce que je demandais, mais probablement une mquander posté approche plus correcte. – andleer

26

Vous n'êtes pas en utilisant un dénombrable comme prévu (pour itérer objets dans une collection). Si vous voulez garder votre code similaire à ce qu'il est maintenant, vous devriez faire quelque chose comme ceci:

static void Main(string[] args) 
{ 
    var Items = GetItems(); 

    foreach (var item in Items) //this will not enter the loop because there are no items in the Items collection 
    { 
      Console.WriteLine(item); 
    } 

    //if you still need to know if there were items, check the Count() extension method 
    if(Items.Count() == 0) 
    { 
     Console.WriteLine("0 items returned"); 
    } 


} 

static IEnumerable<string> GetItems() 
{ 
    if (false) 
    { 
     yield return "Andy"; 
     yield return "Jennifer"; 
    } 

    yield break; 
} 
6

Il n'y a aucun moyen de retourner une valeur nulle IEnumerable<T> à partir d'une méthode iterator. Vous pouvez retourner les valeurs NULL dans la iterator mais pas une valeur nulle IEnumerable<T>

Qu'est-ce que vous pourriez faire bien est une méthode d'emballage qui renvoie soit nul ou appelle à la vraie iterator

static IEnumerable<string> GetItems() { 
    if (false) { 
     return GetItemsCore(); 
    } 
    return null; 
} 

static IEnumerable<string> GetItemsCore() { 
    yield return "Andy"; 
    yield return "Jennifer"; 
} 
12

C'est tout simplement pas encouragé. Quand vous parlez d'une séquence, "null" devrait généralement avoir la même sémantique que "liste vide".

En outre, il est impossible de concevoir le langage pour fonctionner comme vous le souhaitez sans syntaxe supplémentaire, car que se passe-t-il si vous tapez "yield return [whatever]" puis "return null?"

+2

Je suis d'accord, c'est une question d'intention. Le simple fait de passer à travers la fonction sans toucher un «retour de rendement» génèrera une séquence vide. Resposibilty pour vérifier est la séquence vide appartient à autre chose. –

+1

N'a pas été averti que la traversée a généré une séquence vide. Informations très utiles pour moi. Merci. – ryanulit

2

Rien ne vous empêche de faire un yield return null;, si c'est approprié (bien sûr, le type énuméré doit être nullable).

+3

Je serais énervé si je recevais null de ce que je pensais être IEnumerable. Oui, vous pourriez en faire une chaîne nullable dans le cas, mais pourquoi? – scottm

+0

Je n'ai pas dit que je le recommande, je viens de dire que c'est possible. Et btw: Une chaîne est un type de données nullable ... –

+0

Je pense que le gros problème avec cette approche est qu'il n'y a pas de consistance. Que se passe-t-il si certains éléments sont nuls et d'autres ont une valeur? Qu'est-ce que cela indique? – andleer

3

Alors que yield break est propably la meilleure réponse et il n'a pas d'importance puisque vous pouvez toujours faire Items.Count() pour vérifier si elle est supérieure à zéro ou même faire for each on your empty result il pourrait y avoir des situations où cela importe si le résultat est une liste vide ou rien tout et vous voulez toujours utiliser la puissance du rendement.

Dans ce cas, cela aidera.

private IEnumerable<T> YieldItems<T>(IEnumerable<T> items, Action empty = null) 
    { 
     if (items == null) 
     { 
      if (empty != null) empty(); 
      yield break; 
     } 

     foreach (var item in items) 
     { 
      yield return item; 
     } 
    } 

Utilisation

 foreach (var item in YieldItems<string>(null,() => 
     { 
      Console.WriteLine("Empty"); 
     })) 
     { 
      Console.WriteLine(item); 
     } 
Questions connexes