我期望Enumerable.Empty()的实现就是这样:
public static IEnumerable<TResult> Empty<TResult>()
{
yield break;
}
但是实现是这样的:
public static IEnumerable<TResult> Empty<TResult>()
{
return EmptyEnumerable<TResult>.Instance;
}
internal class EmptyEnumerable<TElement>
{
private static volatile TElement[] instance;
public static IEnumerable<TElement> Instance
{
get
{
if (EmptyEnumerable<TElement>.instance == null)
EmptyEnumerable<TElement>.instance = new TElement[0];
return (IEnumerable<TElement>)EmptyEnumerable<TElement>.instance;
}
}
}
为什么实现比一行代码更复杂?返回缓存数组是否有优势而不是(yield)不返回任何元素?
注意:我永远不会依赖方法的实现细节,但我只是好奇。
答案 0 :(得分:6)
编译(使用启用了优化的LINQpad)
public static IEnumerable<TResult> MyEmpty<TResult>()
{
yield break;
}
产生了相当多的代码。
它将创建一个实现IEnumerable
接口的状态机。每次调用MyEmpty
时,它都会创建该类的新实例。返回相同的空数组实例非常便宜。
EmptyEnumerable
的IL代码是:
EmptyEnumerable`1.get_Instance:
IL_0000: volatile.
IL_0002: ldsfld 16 00 00 0A
IL_0007: brtrue.s IL_0016
IL_0009: ldc.i4.0
IL_000A: newarr 04 00 00 1B
IL_000F: volatile.
IL_0011: stsfld 16 00 00 0A
IL_0016: volatile.
IL_0018: ldsfld 16 00 00 0A
IL_001D: castclass 01 00 00 1B
IL_0022: ret
对于MyEmpty
方法,它是:
MyEmpty:
IL_0000: ldc.i4.s FE
IL_0002: newobj 15 00 00 0A
IL_0007: stloc.0
IL_0008: ldloc.0
IL_0009: ret
<MyEmpty>d__0`1.System.Collections.Generic.IEnumerable<TResult>.GetEnumerator:
IL_0000: call System.Environment.get_CurrentManagedThreadId
IL_0005: ldarg.0
IL_0006: ldfld 0E 00 00 0A
IL_000B: bne.un.s IL_0022
IL_000D: ldarg.0
IL_000E: ldfld 0F 00 00 0A
IL_0013: ldc.i4.s FE
IL_0015: bne.un.s IL_0022
IL_0017: ldarg.0
IL_0018: ldc.i4.0
IL_0019: stfld 0F 00 00 0A
IL_001E: ldarg.0
IL_001F: stloc.0
IL_0020: br.s IL_0029
IL_0022: ldc.i4.0
IL_0023: newobj 10 00 00 0A
IL_0028: stloc.0
IL_0029: ldloc.0
IL_002A: ret
<MyEmpty>d__0`1.System.Collections.IEnumerable.GetEnumerator:
IL_0000: ldarg.0
IL_0001: call 11 00 00 0A
IL_0006: ret
<MyEmpty>d__0`1.MoveNext:
IL_0000: ldarg.0
IL_0001: ldfld 0F 00 00 0A
IL_0006: stloc.0 // CS$0$0000
IL_0007: ldloc.0 // CS$0$0000
IL_0008: ldc.i4.0
IL_0009: bne.un.s IL_0012
IL_000B: ldarg.0
IL_000C: ldc.i4.m1
IL_000D: stfld 0F 00 00 0A
IL_0012: ldc.i4.0
IL_0013: ret
<MyEmpty>d__0`1.System.Collections.Generic.IEnumerator<TResult>.get_Current:
IL_0000: ldarg.0
IL_0001: ldfld 12 00 00 0A
IL_0006: ret
<MyEmpty>d__0`1.System.Collections.IEnumerator.Reset:
IL_0000: newobj System.NotSupportedException..ctor
IL_0005: throw
<MyEmpty>d__0`1.System.IDisposable.Dispose:
IL_0000: ret
<MyEmpty>d__0`1.System.Collections.IEnumerator.get_Current:
IL_0000: ldarg.0
IL_0001: ldfld 12 00 00 0A
IL_0006: box 04 00 00 1B
IL_000B: ret
<MyEmpty>d__0`1..ctor:
IL_0000: ldarg.0
IL_0001: call System.Object..ctor
IL_0006: ldarg.0
IL_0007: ldarg.1
IL_0008: stfld 0F 00 00 0A
IL_000D: ldarg.0
IL_000E: call System.Environment.get_CurrentManagedThreadId
IL_0013: stfld 0E 00 00 0A
IL_0018: ret
答案 1 :(得分:2)
这样做是有道理的,因为在这种情况下,对于所有相同类型的空实例,您将需要一个数组,这将需要更少的内存。这就是单个数组实例是静态的原因。
由于不能更改没有元素的数组,因此任何代码都不会弄脏它。