Pregunta

sigue tratando de encontrar dónde iba a utilizar la palabra clave "rendimiento" en una situación real.

veo este hilo sobre el tema

¿Qué es la palabra clave de rendimiento utilizado para en C #?

pero en la respuesta aceptada, tienen esto como un ejemplo donde alguien está iterando alrededor enteros ()

public IEnumerable<int> Integers()
{
yield return 1;
yield return 2;
yield return 4;
yield return 8;
yield return 16;
yield return 16777216;
}

pero ¿por qué no usar simplemente

list<int>

aquí en su lugar. Parece más sencillo ..

¿Fue útil?

Solución

Si se construye y devuelve una lista (dicen que tiene 1 millón de elementos), que es una gran cantidad de memoria, y también de trabajo para crearlo.

A veces, la persona que llama puede querer saber lo que el primer elemento es. O puede ser que desee escribir en un archivo, ya que ellos reciben, en lugar de construir toda la lista en la memoria y luego escribirlo en un archivo.

Es por eso que tiene más sentido usar yield return. No se ve tan diferente a la construcción de toda la lista y devolverlo, pero es muy diferente porque toda la lista no tiene que ser creado en la memoria antes de que la persona que llama puede mirar en el primer punto de la misma.

Cuando la persona que llama dice:

foreach (int i in Integers())
{
   // do something with i
}

Cada vez que el bucle requiere una nueva i, se ejecuta un poco más del código en números enteros (). El código en el que la función está "en pausa" cuando llega una declaración yield return.

Otros consejos

Rendimiento le permite crear métodos que producen datos sin tener que recoger todo lo que hasta antes de regresar. Piense en ello como devolver varios valores a lo largo del camino.

Aquí hay un par de métodos que ilustran el punto

public IEnumerable<String> LinesFromFile(String fileName)
{
    using (StreamReader reader = new StreamReader(fileName))
    {
        String line;
        while ((line = reader.ReadLine()) != null)
            yield return line;
    }
}

public IEnumerable<String> LinesWithEmails(IEnumerable<String> lines)
{
    foreach (String line in lines)
    {
        if (line.Contains("@"))
            yield return line;
    }
}

Ninguno de estos dos métodos se lee todo el contenido del archivo en la memoria, sin embargo, se puede utilizar como esto:

foreach (String lineWithEmail in LinesWithEmails(LinesFromFile("test.txt")))
    Console.Out.WriteLine(lineWithEmail);

Puede utilizar yield para construir cualquier iterador. Eso podría ser una serie con pereza evaluado (leer líneas de un archivo o base de datos, por ejemplo, sin tener que leer todo a la vez, lo que podría ser demasiado para mantener en la memoria), o se puede iterar sobre los datos existentes, tales como un List<T>.

C # en profundidad tiene un capítulo libre (6) sobre todo iterador bloques.

También blogueado muy poco sobre el uso de yield de algoritmos inteligentes de fuerza bruta.

Para un ejemplo del lector de archivos perezoso:

    static IEnumerable<string> ReadLines(string path) {
        using (StreamReader reader = File.OpenText(path)) {
            string line;
            while ((line = reader.ReadLine()) != null) {
                yield return line;
            }
        }
    }

Esto es completamente "perezosa"; no se lee hasta que empiece a enumerar, y sólo una sola línea se almacenan en la memoria cada vez.

Tenga en cuenta que LINQ a Objetos marcas extensa uso de bloques de iterador (yield). Por ejemplo, la extensión Where es esencialmente:

   static IEnumerable<T> Where<T>(this IEnumerable<T> data, Func<T, bool> predicate) {
        foreach (T item in data) {
            if (predicate(item)) yield return item;
        }
    }

Y de nuevo, totalmente vago -. Lo que le permite encadenar múltiples operaciones sin forzar todo lo que va a cargarse en la memoria

rendimiento le permite procesar colecciones que son potencialmente infinito en tamaño, ya que toda la colección no se carga en memoria de una sola vez, a diferencia de un enfoque basado en la Lista. Por ejemplo un IEnumerable <> de todos los números primos podría retrocedió por el algo apropiado para la búsqueda de los números primos, mientras que un enfoque de lista siempre sería finito en tamaño y, por lo tanto incompleta. En este ejemplo, el rendimiento en también permite el procesamiento para el siguiente elemento a ser diferida hasta que se requiera.

Una situación real para mí, es cuando quiero procesar una colección que toma un tiempo para poblar con mayor suavidad.

Imagínese algo en la línea (pseudo código):

public IEnumberable<VerboseUserInfo> GetAllUsers()
{
    foreach(UserId in userLookupList)
    {
        VerboseUserInfo info = new VerboseUserInfo();

        info.Load(ActiveDirectory.GetLotsOfUserData(UserId));
        info.Load(WebSerice.GetSomeMoreInfo(UserId));

        yield return info;
    }
}

En lugar de tener que esperar un minuto para la recogida de poblar antes de que pueda comenzar elementos de procesamiento en el mismo. Voy a ser capaz de empezar de inmediato, y luego informar a la interfaz de usuario como sucede.

No siempre se puede querer usar el rendimiento en lugar de devolver una lista, y en su ejemplo que utilice rendimiento para realmente obtener una lista de números enteros. En función de si desea una lista mutable, o una secuencia inmutable, se puede usar una lista, o un repetidor (o alguna otra colección muttable / inmutable).

Sin embargo, hay beneficios de utilizar el rendimiento.

  • Rendimiento proporciona una manera fácil de construir iteradores evaluados perezosos. (Significado sólo el código para obtener siguiente elemento en la secuencia se ejecuta cuando el método MoveNext () se llama entonces el iterador rendimientos no hacer más cálculos, hasta que el método se llama de nuevo)

  • Rendimiento construye una máquina de estados bajo las sábanas, y esto le ahorra adjudicar de trabajo al no tener que codificar los estados de su generador genérico => código más conciso / simple.

  • Rendimiento optimizado automáticamente construye y el hilo iteradores seguras, que nos evita los detalles sobre cómo construirlos.

  • El rendimiento es mucho más potente de lo que parece a primera vista y se puede utilizar para mucho más que sólo la construcción de iteradores simples, echa un vistazo a este vídeo para ver Jeffrey Richter y su AsyncEnumerator y cómo rendimiento se utiliza maquillaje de codificación utilizando el patrón de async fácil.

Es posible que desee iterar a través de varias colecciones:

public IEnumerable<ICustomer> Customers()
{
        foreach( ICustomer customer in m_maleCustomers )
        {
            yield return customer;
        }

        foreach( ICustomer customer in m_femaleCustomers )
        {
            yield return customer;
        }

        // or add some constraints...
        foreach( ICustomer customer in m_customers )
        {
            if( customer.Age < 16 )
            {
                yield return customer;
            }
        }

        // Or....            
        if( Date.Today == 1 )
        {
            yield return m_superCustomer;
        }

}

Estoy de acuerdo con todo el mundo ha dicho aquí sobre el uso de la evaluación y la memoria perezoso y quería añadir otro escenario donde he encontrado los iteradores utilizando la palabra clave yield útil. Me he encontrado algunos casos en los que tengo que hacer una secuencia de procesamiento potencialmente costosa en algunos datos, donde es extremadamente útil el uso de iteradores. En lugar de procesar el archivo completo de forma inmediata, o rodar mi propia canalización de procesamiento, simplemente puedo usar iteradores algo como esto:

IEnumerable<double> GetListFromFile(int idxItem)
{
    // read data from file
    return dataReadFromFile;
}

IEnumerable<double> ConvertUnits(IEnumerable<double> items)
{
    foreach(double item in items)
        yield return convertUnits(item);
}

IEnumerable<double> DoExpensiveProcessing(IEnumerable<double> items)
{
    foreach(double item in items)
        yield return expensiveProcessing(item);
}

IEnumerable<double> GetNextList()
{
    return DoExpensiveProcessing(ConvertUnits(GetListFromFile(curIdx++)));
}

La ventaja aquí es que al mantener la entrada y salida de todas las funciones IEnumerable<double>, mi canalización de procesamiento es totalmente componibles, fácil de leer, y perezoso evaluado para que sólo tenga que hacer el tratamiento que realmente necesita hacer. Esto me permite poner casi todos los de mi procesamiento en el hilo GUI sin afectar la capacidad de respuesta, así que no tiene que preocuparse acerca de los problemas de roscado.

Se me ocurrió esto a superar .net inconveniente tener que manualmente Lista copia en profundidad.

Yo uso este:

static public IEnumerable<SpotPlacement> CloneList(List<SpotPlacement> spotPlacements)
{
    foreach (SpotPlacement sp in spotPlacements)
    {
        yield return (SpotPlacement)sp.Clone();
    }
}

Y en otro lugar:

public object Clone()
{
    OrderItem newOrderItem = new OrderItem();
    ...
    newOrderItem._exactPlacements.AddRange(SpotPlacement.CloneList(_exactPlacements));
    ...
    return newOrderItem;
}

He intentado llegar a oneliner que hace esto, pero no es posible, debido a un rendimiento no trabaja dentro de los bloques método anónimo.

EDIT:

Mejor aún, utilice clonador lista genérica:

class Utility<T> where T : ICloneable
{
    static public IEnumerable<T> CloneList(List<T> tl)
    {
        foreach (T t in tl)
        {
            yield return (T)t.Clone();
        }
    }
}

El método utilizado por yield de salvar la memoria mediante el procesamiento de artículos sobre la marcha es agradable, pero en realidad es sólo azúcar sintáctico. Que ha estado presente durante mucho tiempo. En cualquier lenguaje que tiene la función de interfaz o incluso punteros (C y montaje) se puede obtener el mismo efecto usando una función de devolución de llamada / interfaz.

Esta materia de lujo:

static IEnumerable<string> GetItems()
{
    yield return "apple";
    yield return "orange";
    yield return "pear";
}

foreach(string item in GetItems())
{
    Console.WriteLine(item);
}

es básicamente equivalente a la antigua usanza:

interface ItemProcessor
{
    void ProcessItem(string s);
};

class MyItemProcessor : ItemProcessor
{
    public void ProcessItem(string s)
    {
        Console.WriteLine(s);
    }
};

static void ProcessItems(ItemProcessor processor)
{
    processor.ProcessItem("apple");
    processor.ProcessItem("orange");
    processor.ProcessItem("pear");
}

ProcessItems(new MyItemProcessor());
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top