2008-10-23 7 views
380

He visto esta sintaxis en MSDN: yield break, pero no sé lo que hace. ¿Alguien sabe?¿Qué significa "ceder el rendimiento"; hacer en C#?

+16

Sin embargo, la documentación de MSDN lo menciona pero no lo explica. Esa es una mala manera de molestar a un desarrollador hambriento de conocimiento. – Phil

+2

Retorno de rendimiento elimina la necesidad de una lista de respaldo, es decir, no necesita codificar algo como 'MyList.Add (...)' simplemente haga 'yield return ...'. Si necesita salir del ciclo prematuramente y devolver la lista de respaldo virtual, use 'yield break;' –

+0

Asesoramiento sólido, de "The Muffin Man" –

Respuesta

418

Se especifica que un iterador ha llegado a su fin. Puede pensar en yield break como una declaración return que no devuelve un valor.

Por ejemplo, si se define una función como un iterador, el cuerpo de la función puede tener este aspecto:

for (int i = 0; i < 5; i++) 
{ 
    yield return i; 
} 

Console.Out.WriteLine("You will see me"); 

Tenga en cuenta que después de que el bucle ha completado todos sus ciclos, la última línea es ejecutado y Verás el mensaje en tu aplicación de consola.

O como este con yield break:

int i = 0; 
while (true) 
{ 
    if (i < 5) 
    { 
     yield return i; 
    } 
    else 
    { 
     // note that i++ will not be executed after this 
     yield break; 
    } 
    i++; 
} 

Console.Out.WriteLine("Won't see me"); 

En este caso, la última declaración nunca se ejecuta porque dejamos la función temprana.

+3

¿Podría ser simplemente 'break' en lugar de' yield break' en tu ejemplo anterior? El compilador no se queja de eso. – orad

+48

@orad un simple 'break' en este caso detendría el ciclo, pero no abortaría la ejecución del método, por lo tanto, se ejecutaría la última línea y se vería realmente el texto" Will not me me ". –

+0

@ DamirZekić, ¿completa el iterador todos los ciclos en caso de ruptura? – machinarium

45

Finaliza un bloque de iteradores (por ejemplo, dice que no hay más elementos en IEnumerable).

+1

con [+1] - aunque académicamente no hay iteradores en .NET . solo enumeradores (una dirección, adelante) –

+0

@Shaun Wilson, pero con la palabra clave 'yield' puede iterar la colección en ambas direcciones, además puede tomar cada elemento siguiente de la colección no en una fila – monstr

+0

@monstr puede iterar la colección en cualquier moda y no necesita 'rendimiento' para hacerlo. No digo que estés equivocado, veo lo que estás sugiriendo; pero, académicamente no hay iteradores en .NET, solo enumeradores (una dirección, hacia adelante); a diferencia de stdC++, no hay un "marco de iterador genérico" definido en el CTS/CLR.LINQ ayuda a cerrar la brecha con los métodos de extensión que utilizan 'yield return' ** y también los métodos de devolución de llamada **, pero son métodos de extensión de primera clase, no iteradores de primera clase. el 'IEnumerable' resultante no puede iterar por sí mismo en ninguna dirección que no sea la de la persona que llama. –

4

Si lo que quiere decir con "lo que hace ceder ruptura hacer realmente", es "cómo funciona" - Ver el blog de Raymond Chen para más detalles http://blogs.msdn.com/oldnewthing/archive/2008/08/12/8849519.aspx

C# iteradores generan un código muy complicado.

+10

Bueno, solo son complicados si te importa el código en el que están compilados. El código que los usa es bastante simple. –

+0

@Robert, eso es lo que quise decir, así que actualicé la respuesta para incluir tu comentario –

27

Indica al repetidor que se ha llegado al final.

A modo de ejemplo:

public interface INode 
{ 
    IEnumerable<Node> GetChildren(); 
} 

public class NodeWithTenChildren : INode 
{ 
    private Node[] m_children = new Node[10]; 

    public IEnumerable<Node> GetChildren() 
    { 
     for(int n = 0; n < 10; ++n) 
     { 
      yield return m_children[ n ]; 
     } 
    } 
} 

public class NodeWithNoChildren : INode 
{ 
    public IEnumerable<Node> GetChildren() 
    { 
     yield break; 
    } 
} 
10

Todo el tema de los bloques de iteradores está bien cubierto en este free sample chapter del libro de Jon Skeet C# in Depth.

+0

Acabas de venderme una copia. ¡Gracias! :-) –

18

yield básicamente hace que un método IEnumerable<T> se comporte de manera similar a un hilo programado cooperativamente (en lugar de preventivamente).

yield return es como un hilo que llama a una función de "programación" o "suspensión" para ceder el control de la CPU. Al igual que un hilo, el método IEnumerable<T> recupera los controles en el punto inmediatamente posterior, con todas las variables locales que tienen los mismos valores que tenían antes de abandonar el control.

yield break es como un hilo que llega al final de su función y finaliza.

La gente habla de una "máquina de estado", pero una máquina de estado es todo lo que realmente es un "hilo". Un hilo tiene algún estado (es decir, valores de variables locales), y cada vez que está programado toma algunas acciones para alcanzar un nuevo estado. El punto clave sobre yield es que, a diferencia de los subprocesos del sistema operativo al que estamos acostumbrados, el código que lo usa se congela en el tiempo hasta que la iteración se adelanta o finaliza manualmente.

6

Aquí http://www.alteridem.net/2007/08/22/the-yield-statement-in-c/ es muy buen ejemplo:

 
public static IEnumerable<int> Range(int min, int max) 
{ 
    while (true) 
    { 
     if (min >= max) 
     { 
     yield break; 
     } 
     yield return min++; 
    } 
} 

y explicación, que si una declaración yield break es golpeado dentro de un método, la ejecución de ese método detiene sin retorno. Hay algunas situaciones de tiempo, cuando no desea dar ningún resultado, entonces puede usar el límite de rendimiento.

0

La palabra clave yield se utiliza junto con la palabra clave return para proporcionar un valor al objeto enumerador. yield return especifica el valor o valores devueltos. Cuando se alcanza la declaración de rendimiento, se almacena la ubicación actual. La ejecución se reinicia desde esta ubicación la próxima vez que se llame al iterador.

para explicar el significado usando un ejemplo:

public IEnumerable<int> SampleNumbers() 
    { 
     int counter = 0; 
     yield return counter; 

     counter = counter + 2; 

     yield return counter; 

     counter = counter + 3; 

     yield return counter ; 
    } 

Valores devueltos cuando esto se repite son: 0, 2, 5.

Es importante señalar que contador variable en este ejemplo es una variable local. Después de la segunda iteración que devuelve el valor de 2, tercera iteración comienza desde donde lo dejó antes, preservando al mismo tiempo el valor anterior de la variable local llamada contador que era 2.

+10

No explicó lo que 'yield break 'hace –

+0

No creo que' yield return' en realidad sea compatible con la devolución de valores múltiples. Tal vez eso no es lo que realmente quiso decir, pero así es como lo leí. – Sam

+0

Sam: el método SampleNumbers con múltiples declaraciones de rendimiento de retorno funciona de hecho, el valor del iterador se devuelve inmediatamente y la ejecución se reanuda cuando se solicita el siguiente valor. He visto a gente terminar un método como este con "yield break", pero es innecesario. Al tocar el final del método también finaliza el iterador –

4

La declaración yield break hace que la enumeración que se detuviera. En efecto, yield break completa la enumeración sin devolver ningún elemento adicional.

Considere que en realidad hay dos maneras en que un método iterador podría dejar de iterar. En un caso, la lógica del método podría salir naturalmente del método después de devolver todos los elementos. Aquí está un ejemplo:

IEnumerable<uint> FindPrimes(uint startAt, uint maxCount) 
{ 
    for (var i = 0UL; i < maxCount; i++) 
    { 
     startAt = NextPrime(startAt); 
     yield return startAt; 
    } 

    Debug.WriteLine("All the primes were found."); 
} 

En el ejemplo anterior, el método iterador, naturalmente, detener la ejecución de una vez maxCount números primos se han encontrado.

La instrucción yield break es otra forma de que el iterador deje de enumerar. Es una forma de salir de la enumeración temprano. Este es el mismo método que el anterior. Esta vez, el método tiene un límite en la cantidad de tiempo que puede ejecutar el método.

IEnumerable<uint> FindPrimes(uint startAt, uint maxCount, int maxMinutes) 
{ 
    var sw = System.Diagnostics.Stopwatch.StartNew(); 
    for (var i = 0UL; i < maxCount; i++) 
    { 
     startAt = NextPrime(startAt); 
     yield return startAt; 

     if (sw.Elapsed.TotalMinutes > maxMinutes) 
      yield break; 
    } 

    Debug.WriteLine("All the primes were found."); 
} 

Observe la llamada al yield break. En efecto, está saliendo de la enumeración temprano.

Tenga en cuenta también que el yield break funciona de manera diferente a simplemente un break. En el ejemplo anterior, yield break sale del método sin realizar la llamada al Debug.WriteLine(..).