[c#]Enumerable.Empty() 为什么返回一个空数组?

标签: Linq C#
发布时间: 2017/2/28 22:47:23
注意事项: 本文中文内容可能为机器翻译,如要查看英文原文请点击上面连接.

我预计 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;
        }
    }
}

为什么执行都是比只是一行代码更复杂吗?它有优势要返回一个数组,缓存并不 (收益率) 返回任何元素吗?

注︰ 我将永远不会依赖于实现细节的一种方法,但我只是好奇。

解决方法 1:

编译 (使用 LINQpad 启用了优化)

public static IEnumerable<TResult> MyEmpty<TResult>()
{
    yield break;
}

在许多代码的结果。

它将创建一个状态机实现 IEnumerable 接口。每次你调用 MyEmpty 它将创建该类的新实例。返回一个空数组的相同实例是相当便宜的。

IL 代码为 EmptyEnumerable 是︰

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         
赞助商