C#: How to translate the Yield Keyword
- What would the MSDN sample look like without the yield keyword? You may use any example if you perfer. I would just like to understand what is going on under the hood.
- Is the yield operator eagerly or lazily evaluated?
Sample:
using System;
using System.Collections;
public class List
{
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);
}
}
}
- MSDN - Yield Keyword
If the yield operator is eagerly evaluated here is my guess:
public static IEn开发者_如何学Pythonumerable Power(int number, int exponent)
{
int counter = 0;
int result = 1;
List<int> powers;
while (counter++ < exponent)
{
result = result * number;
powers.add(result);
}
return powers;
}
I have no clue what it might look like if the yield operator is lazily evaluated.
Update: Reflector gives this:
public class List
{
// Methods
public List();
private static void Main();
public static IEnumerable Power(int number, int exponent);
// Nested Types
[CompilerGenerated]
private sealed class <Power>d__0 : IEnumerable<object>, IEnumerable, IEnumerator<object>, IEnumerator, IDisposable
{
// Fields
private int <>1__state;
private object <>2__current;
public int <>3__exponent;
public int <>3__number;
private int <>l__initialThreadId;
public int <counter>5__1;
public int <result>5__2;
public int exponent;
public int number;
// Methods
[DebuggerHidden]
public <Power>d__0(int <>1__state);
private bool MoveNext();
[DebuggerHidden]
IEnumerator<object> IEnumerable<object>.GetEnumerator();
[DebuggerHidden]
IEnumerator IEnumerable.GetEnumerator();
[DebuggerHidden]
void IEnumerator.Reset();
void IDisposable.Dispose();
// Properties
object IEnumerator<object>.Current { [DebuggerHidden] get; }
object IEnumerator.Current { [DebuggerHidden] get; }
}
}
IEnumerator<object> IEnumerable<object>.GetEnumerator()
{
List.<Power>d__0 d__;
if ((Thread.CurrentThread.ManagedThreadId == this.<>l__initialThreadId) && (this.<>1__state == -2))
{
this.<>1__state = 0;
d__ = this;
}
else
{
d__ = new List.<Power>d__0(0);
}
d__.number = this.<>3__number;
d__.exponent = this.<>3__exponent;
return d__;
}
private bool MoveNext()
{
switch (this.<>1__state)
{
case 0:
this.<>1__state = -1;
this.<counter>5__1 = 0;
this.<result>5__2 = 1;
while (this.<counter>5__1++ < this.exponent)
{
this.<result>5__2 *= this.number;
this.<>2__current = this.<result>5__2;
this.<>1__state = 1;
return true;
Label_0065:
this.<>1__state = -1;
}
break;
case 1:
goto Label_0065;
}
return false;
}
First off, yield is not an operator. yield return and yield break are statements.
There are plenty of articles available on how the compiler implements iterator blocks. Start by reading the C# specification section on iterator blocks; it gives some suggestions for how an implementer of C# might want to go about it.
Next read Raymond Chen's series "The implementation of iterators in C# and its consequences"
http://www.bing.com/search?q=raymond+chen+the+implementation+of+iterators
Next, read Jon Skeet's book chapter on the subject:
http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx
If after all that you are still interested then read my series on the design factors that went into this feature:
http://blogs.msdn.com/b/ericlippert/archive/tags/iterators/
Back in the good old days, before we had the yield operator, we used to write classes which implemented IEnumerator.
class PowerEnumerator : IEnumerator<int>
{
private int _number;
private int _exponent;
private int _current = 1;
public PowerEnumerator(int number, int exponent)
{
_number = number;
_exponent = exponent;
}
public bool MoveNext()
{
_current *= number;
return _exponent-- > 0;
}
public int Current
{
get
{
if (_exponent < 0) throw new InvalidOperationException();
return _current;
}
}
}
Or something like that. It wasn't fun, let me tell you.
- Let .NET Reflector decompile it. It's a generic solution (a state machine actually), but quite complex, > 20 lines of codes if I remember correctly.
- Lazy. That's the point why
yield
can be quite efficient.
- It would be a custom implementation of
IEnumerable<T>
, not leaning on an existing implementation such asList<T>
- Lazily.
More info available here.
精彩评论