986 votes

La bonne Utilisation de taux de retour

Le rendement mot-clé est un de ces mots-clés en C# qui continue à mystifier moi et je n'ai jamais été convaincu que je suis de l'utiliser correctement.

De deux morceaux de code, qui est le préféré et pourquoi?

Version 1: taux de retour à l'Aide de

public static IEnumerable<Product> GetAllProducts()
{
    using (AdventureWorksEntities db = new AdventureWorksEntities())
    {
        var products = from product in db.Product
                       select product;

        foreach (Product product in products)
        {
            yield return product;
        }
    }
}

Version 2: Retour à la liste

public static IEnumerable<Product> GetAllProducts()
{
    using (AdventureWorksEntities db = new AdventureWorksEntities())
    {
        var products = from product in db.Product
                       select product;

        return products.ToList<Product>();
    }
}

863voto

abelenky Points 28063

J'ai tendance à utiliser de rendement-retour quand j'ai calculer l'élément suivant dans la liste (ou même le prochain groupe d'éléments).

À l'aide de votre Version 2, vous devez disposer de la liste complète avant de la retourner. En utilisant de rendement-retour, vous avez vraiment besoin d'avoir le point suivant avant de retourner.

Entre autres choses, cela permet de partager le coût de calcul des calculs complexes sur un plus grand laps de temps. Par exemple, si la liste est relié à une interface graphique et l'utilisateur ne va jamais à la dernière page, on ne parle jamais de la finale des éléments dans la liste.

Un autre cas où le rendement de retour est préférable si l'interface IEnumerable représente un ensemble infini. Examiner la liste des Nombres premiers, ou une liste infinie de nombres aléatoires. Vous ne pouvez jamais vous retourner la totalité des IEnumerable à la fois, si vous utilisez de rendement-retour pour revenir à la liste de manière incrémentale.

Dans votre exemple, vous avez la liste complète des produits, j'aimerais utiliser la Version 2.

736voto

Anar Khalilov Points 1922

Le remplissage temporaire de la liste, c'est comme le téléchargement de la vidéo en entier, tandis que l'utilisation de rendement, c'est comme le streaming vidéo.

82voto

Kache Points 2182

Comme un exemple conceptuel pour comprendre quand vous devez utiliser yield, disons que la méthode ConsumeLoop() traite les éléments retournés/a abouti en ProduceList():

void ConsumeLoop() {
    foreach (Consumable item in ProduceList())        // might have to wait here
        item.Consume();
}

IEnumerable<Consumable> ProduceList() {
    while (KeepProducing())
        yield return ProduceExpensiveConsumable();    // expensive
}

Sans yield, l'appel à l' ProduceList() peut prendre beaucoup de temps parce que vous avez à compléter la liste avant de retourner:

//pseudo-assembly
Produce consumable[0]                   // expensive operation, e.g. disk I/O
Produce consumable[1]                   // waiting...
Produce consumable[2]                   // waiting...
Produce consumable[3]                   // completed the consumable list
Consume consumable[0]                   // start consuming
Consume consumable[1]
Consume consumable[2]
Consume consumable[3]

À l'aide de yield, il devient réarrangés, sorte de travail "en parallèle":

//pseudo-assembly
Produce consumable[0]
Consume consumable[0]                   // immediately Consume
Produce consumable[1]
Consume consumable[1]                   // consume next
Produce consumable[2]
Consume consumable[2]                   // consume next
Produce consumable[3]
Consume consumable[3]                   // consume next

Et enfin, comme beaucoup avant l'avons déjà suggéré, vous devez utiliser la Version 2, car vous avez déjà la liste de toute façon.

76voto

Jon Skeet Points 692016

Vous pouvez réécrire la première version:

public static IEnumerable<Product> GetAllProducts()
{
    using (AdventureWorksEntities db = new AdventureWorksEntities())
    {
        return db.Product.ToList();
    }
}

ou éventuellement utiliser:

public static IEnumerable<Product> GetAllProducts()
{
    using (AdventureWorksEntities db = new AdventureWorksEntities())
    {
        return db.Product.Select(p => p).ToList();
    }
}

Dans le cas de LINQ to SQL, ça ne fait pas beaucoup de différence parce que les résultats ont tous été récupérés de toute façon, je crois. Dans d'autres cas, la diffusion en continu les données de fait toute la différence entre une solution pratique et pas.

Pour plus d'informations sur ce qu' yield , en passant, lisez le chapitre 6 de C# dans la Profondeur, qui est disponible gratuitement sur son Manning site. J'ai aussi un couple d'articles à ce sujet:

26voto

Robert Rossney Points 43767

Cela va sembler bizarre suggestion, mais j'ai appris à utiliser l' yield de mots clés de C# par la lecture d'une présentation sur les générateurs en Python: David M. Beazley de l' http://www.dabeaz.com/generators/Generators.pdf. Vous n'avez pas besoin de connaître beaucoup de Python pour comprendre la présentation, je ne l'ai pas. Je l'ai trouvé très utile pour expliquer non seulement comment les générateurs de travail, mais pourquoi vous devriez faire attention.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X