499 votos

Uso adecuado de producción de retorno

El rendimiento de una palabra clave es una de esas palabras clave en C# que sigue a desconcertar a mí y nunca he estado seguro de que estoy utilizando correctamente.

De las dos siguientes partes de código, que es el preferido y por qué?

Versión 1: el Uso de rendimiento

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;
        }
    }
}

Versión 2: el Retorno de la lista

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

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

519voto

abelenky Puntos 28063

Yo tiendo a usar rendimiento de regreso cuando me calcular el siguiente elemento en la lista (o incluso el siguiente grupo de artículos).

El uso de la Versión 2, debe tener la lista completa antes de regresar. Mediante el uso de rendimiento-retorno, que realmente sólo se necesita tener el siguiente artículo antes de regresar.

Entre otras cosas, esto ayuda a difundir el coste computacional de cálculos complejos en un mayor plazo de tiempo. Por ejemplo, si la lista está conectado a una interfaz gráfica de usuario y el usuario nunca va a la última página, usted nunca calcular la final de los elementos de la lista.

Otro caso en el que el rendimiento de retorno es preferible si el IEnumerable representa un conjunto infinito. Considere la lista de Números Primos, o una lista infinita de números aleatorios. Usted no puede regresar a la plena IEnumerable a la vez, por lo que el uso de rendimiento-return para volver a la lista de forma incremental.

En el ejemplo particular, tienen la lista completa de los productos, así que me gustaría usar la Versión 2.

228voto

Anar Khalilov Puntos 1922

Rellenar una lista temporal es como descarga el video completo, mientras que utilizando el rendimiento es como ese video streaming.

76voto

Jon Skeet Puntos 692016

Usted puede volver a escribir la primera versión:

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

o posiblemente de uso:

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

En el caso de LINQ to SQL, no hace mucho la diferencia, ya que los resultados serán todos han sido obtenidos de todos modos, creo. En otros casos, las secuencias de datos que hace la diferencia entre una solución práctica y no.

Para obtener más información sobre qué yield sí, por el camino, lea el capítulo 6 de C# en la Profundidad, que está disponible de forma gratuita en su Dotación sitio. También tengo un par de artículos sobre:

31voto

Kache Puntos 2182

Como un ejemplo conceptual para la comprensión de cuándo se debe usar yield, digamos que el método ConsumeLoop() procesos los artículos devueltos/producido por ProduceList():

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

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

Sin yield, la llamada a ProduceList() podría tomar un largo tiempo, porque tienes que completa la lista antes de regresar:

//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]

Utilizando yield, se convierte en cambiados de lugar, tipo de trabajo "en paralelo":

//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

Y por último, como muchos antes ya han sugerido, usted debe utilizar la Versión 2 porque ya tiene la lista completa de todos modos.

20voto

Robert Rossney Puntos 43767

Esto va a parecer una sugerencia extraña, pero he aprendido a usar el yield clave en C# mediante la lectura de una presentación sobre los generadores en Python: http://www.dabeaz.com/generators/Generators.pdf de David M. Beazley. No necesitas saber mucho Python para entender la presentación - que no. Me pareció muy útil para explicar no sólo cómo funcionan los generadores pero por qué debería importarte.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X