c# - que - ¿Puede alguien desmitificar la palabra clave de rendimiento?
yield return list c# (9)
Con mucho, la mejor explicación de esto (que he visto) es el libro de Jon Skeet, ¡y ese capítulo es gratis! Capítulo 6, C # en profundidad . No hay nada que pueda agregar aquí que no esté cubierto.
Entonces compra el libro; Serás un mejor programador de C # para ello.
P: ¿Por qué no escribí una respuesta más larga aquí (parafraseado de los comentarios); sencillo. Como observa Eric Lippert ( here ), la construcción de yield
(y la magia que va detrás de ella) es el bit de código más complejo del compilador de C # , y tratar de describirlo en una breve respuesta aquí es, en el mejor de los casos, ingenuo. Hay tantos matices para yield
que IMO es mejor referirse a un recurso preexistente (y completamente calificado).
El blog de Eric ahora tiene 7 entradas (y esas son solo las recientes) que analizan el yield
. Tengo un gran respeto por Eric, pero su blog es probablemente más apropiado como "información adicional" para las personas que se sienten cómodas con el tema ( yield
en este caso), ya que generalmente describe muchas de las consideraciones de diseño de fondo. Mejor hecho en el contexto de una base razonable.
(y sí, el capítulo 6 se descarga; lo verifiqué ...)
He visto que la palabra clave de rendimiento se usa mucho en Stack Overflow y en los blogs. Yo no uso LINQ . ¿Puede alguien explicar la palabra clave de rendimiento?
Sé que existen preguntas similares. Pero ninguno explica realmente cuál es su uso en un lenguaje sencillo y simple.
Déjame añadir a todo esto. El rendimiento no es una palabra clave. Solo funcionará si usas "rendimiento de rendimiento" aparte de que funcionará como una variable normal.
Se utiliza para devolver iterador de una función. Puedes buscar más en eso. Recomiendo buscar "Returning Array vs Iterator"
Echa un vistazo a la documentación de MSDN y el ejemplo. Es esencialmente una forma fácil de crear un iterador en C #.
public class List
{
//using System.Collections;
public static IEnumerable Power(int number, int exponent)
{
int counter = 0;
int result = 1;
while (counter++ < exponent)
{
result = result * number;
yield return result;
}
}
static void Main()
{
// Display powers of 2 up to the exponent 8:
foreach (int i in Power(2, 8))
{
Console.Write("{0} ", i);
}
}
}
En un esfuerzo por desmitificar, evitaré hablar de iteradores, ya que ellos mismos podrían ser parte del misterio.
Las declaraciones de rendimiento y rendimiento se utilizan con más frecuencia para proporcionar una "evaluación diferida" de la colección.
Lo que esto significa es que cuando obtienes el valor de un método que usa rendimiento de rendimiento, la colección de cosas que intentas obtener todavía no existe (todavía está esencialmente vacía). A medida que los recorres (usando foreach) ejecutará el método en ese momento y obtendrá el siguiente elemento en la enumeración.
Ciertas propiedades y métodos harán que toda la enumeración se evalúe a la vez (como "Count").
Este es un ejemplo rápido de la diferencia entre devolver una colección y devolver el rendimiento:
string[] names = { "Joe", "Jim", "Sam", "Ed", "Sally" };
public IEnumerable<string> GetYieldEnumerable()
{
foreach (var name in names)
yield return name;
}
public IEnumerable<string> GetList()
{
var list = new List<string>();
foreach (var name in names)
list.Add(name);
return list;
}
// we''re going to execute the GetYieldEnumerable() method
// but the foreach statement inside it isn''t going to execute
var yieldNames = GetNamesEnumerable();
// now we''re going to execute the GetList() method and
// the foreach method will execute
var listNames = GetList();
// now we want to look for a specific name in yieldNames.
// only the first two iterations of the foreach loop in the
// GetYieldEnumeration() method will need to be called to find it.
if (yieldNames.Contains("Jim")
Console.WriteLine("Found Jim and only had to loop twice!");
// now we''ll look for a specific name in listNames.
// the entire names collection was already iterated over
// so we''ve already paid the initial cost of looping through that collection.
// now we''re going to have to add two more loops to find it in the listNames
// collection.
if (listNames.Contains("Jim"))
Console.WriteLine("Found Jim and had to loop 7 times! (5 for names and 2 for listNames)");
También se puede utilizar si necesita obtener una referencia a la enumeración antes de que los datos de origen tengan valores. Por ejemplo, si la colección de nombres no estaba completa para comenzar con:
string[] names = { "Joe", "Jim", "Sam", "Ed", "Sally" };
public IEnumerable<string> GetYieldEnumerable()
{
foreach (var name in names)
yield return name;
}
public IEnumerable<string> GetList()
{
var list = new List<string>();
foreach (var name in names)
list.Add(name);
return list;
}
var yieldNames = GetNamesEnumerable();
var listNames = GetList();
// now we''ll change the source data by renaming "Jim" to "Jimbo"
names[1] = "Jimbo";
if (yieldNames.Contains("Jimbo")
Console.WriteLine("Found Jimbo!");
// Because this enumeration was evaluated completely before we changed "Jim"
// to "Jimbo" it isn''t going to be found
if (listNames.Contains("Jimbo"))
// this can''t be true
else
Console.WriteLine("Couldn''t find Jimbo, because he wasn''t there when I was evaluated.");
La serie de Eric White sobre programación funcional bien vale la pena leerla en su totalidad, pero la entrada en Yield es una explicación tan clara como he visto.
La palabra clave de yield
es una forma conveniente de escribir un IEnumerator
. Por ejemplo:
public static IEnumerator<int> Range(int from, int to)
{
for (int i = from; i < to; i++)
{
yield return i;
}
}
Es transformado por el compilador de C # a algo similar a:
public static IEnumerator<int> Range(int from, int to)
{
return new RangeEnumerator(from, to);
}
class RangeEnumerator : IEnumerator<int>
{
private int from, to, current;
public RangeEnumerator(int from, int to)
{
this.from = from;
this.to = to;
this.current = from;
}
public bool MoveNext()
{
this.current++;
return this.current < this.to;
}
public int Current
{
get
{
return this.current;
}
}
}
La palabra clave de yield
se usa con métodos que devuelven IEnumerable<T>
o IEnumerator<T>
y hace que el compilador genere una clase que implemente la tubería necesaria para usar el iterador. P.ej
public IEnumerator<int> SequenceOfOneToThree() {
yield return 1;
yield return 2;
yield return 3;
}
Dado lo anterior, el compilador generará una clase que implementa IEnumerator<int>
, IEnumerable<int>
e IDisposable
(en realidad también implementará las versiones no genéricas de IEnumerable
e IEnumerator
).
Esto le permite llamar al método SequenceOfOneToThree
en un bucle foreach
como este
foreach(var number in SequenceOfOneToThree) {
Console.WriteLine(number);
}
Un iterador es una máquina de estado, por lo que cada vez que se denomina yield
, se registra la posición en el método. Si el iterador se mueve al siguiente elemento, el método se reanuda justo después de esta posición. Así que la primera iteración devuelve 1 y marca esa posición. El siguiente iterador se reanuda justo después de uno y, por lo tanto, devuelve 2 y así sucesivamente.
No hace falta decir que puedes generar la secuencia de la forma que quieras, para que no tengas que codificar los números como lo hice yo. Además, si desea romper el bucle, puede utilizar la yield break
.
Se me ocurrió esto para superar una deficiencia de .NET al tener que copiar manualmente la lista.
Yo uso esto:
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;
}
Traté de encontrar un oneliner que hiciera esto, pero no es posible, debido a que el rendimiento no funciona dentro de los bloques de métodos anónimos.
EDITAR:
Mejor aún, use un clonador de lista genérico:
class Utility<T> where T : ICloneable
{
static public IEnumerable<T> CloneList(List<T> tl)
{
foreach (T t in tl)
{
yield return (T)t.Clone();
}
}
}
MSDN no está directamente relacionado con LINQ, sino con los bloques de iteradores . El artículo de MSDN vinculado proporciona gran detalle sobre esta característica del lenguaje. Ver especialmente la sección Uso de iteradores . Para obtener información detallada sobre los bloques de iteradores, consulte las publicaciones recientes del blog de Eric Lippert sobre la característica. Para el concepto general, vea el article Wikipedia sobre iteradores.