c# - example - ¿Cómo es un rendimiento enumerable?
remarks c# (3)
Cuando el compilador ve yield return
o yield break
, toma la función y cubre la lógica en una clase que implementa una máquina de estados. Una instancia de esta clase se devuelve cuando se llama el método.
csharpindepth.com/Articles/Chapter6/… tiene una sección sobre cómo se ve el código.
Estaba jugando con el yield
y con IEnumerable
y ahora tengo curiosidad de por qué o cómo funciona el siguiente fragmento de IEnumerable
:
public class FakeList : IEnumerable<int>
{
private int one;
private int two;
public IEnumerator<int> GetEnumerator()
{
yield return one;
yield return two;
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Ahora, ¿cómo el compilador gira esto?
public IEnumerator<int> GetEnumerator()
{
yield return one;
yield return two;
}
en un IEnumerator<int>
?
Cuando se utiliza el yield return
, el compilador genera una clase de enumerador para usted. Por lo tanto, el código real que se usa es mucho más complejo que solo dos declaraciones de retorno. El compilador agrega todo el código necesario para devolverle un enumerador, que itera sobre los resultados del yield return
.
Este es el código generado de su FakeList.GetEnumerator()
:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
public class FakeList : IEnumerable<int>, IEnumerable
{
private int one;
private int two;
[IteratorStateMachine(typeof(<GetEnumerator>d__2))]
public IEnumerator<int> GetEnumerator()
{
yield return this.one;
yield return this.two;
}
IEnumerator IEnumerable.GetEnumerator() =>
this.GetEnumerator();
[CompilerGenerated]
private sealed class <GetEnumerator>d__2 : IEnumerator<int>, IDisposable, IEnumerator
{
private int <>1__state;
private int <>2__current;
public FakeList <>4__this;
[DebuggerHidden]
public <GetEnumerator>d__2(int <>1__state)
{
this.<>1__state = <>1__state;
}
private bool MoveNext()
{
switch (this.<>1__state)
{
case 0:
this.<>1__state = -1;
this.<>2__current = this.<>4__this.one;
this.<>1__state = 1;
return true;
case 1:
this.<>1__state = -1;
this.<>2__current = this.<>4__this.two;
this.<>1__state = 2;
return true;
case 2:
this.<>1__state = -1;
return false;
}
return false;
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
[DebuggerHidden]
void IDisposable.Dispose()
{
}
int IEnumerator<int>.Current =>
this.<>2__current;
object IEnumerator.Current =>
this.<>2__current;
}
}
¿Ves la clase <GetEnumerator>d__2
? Eso se genera en base a sus dos yield return
s.
Es un generador. No puedo decir cómo funciona el compilador detrás, pero cuando lo haces:
yield return x;
No dejará la función como el retorno clásico, sino que devolverá un valor y luego continuará la ejecución de la función.
Si recuerdo bien, hay una pequeña diferencia entre un enumerable real y este, si no usa un método para transformar su generador en un enumerable verdadero (dependiendo de lo que esté tratando de lograr), puede tener algunos problemas.