2014-11-12 15 views
9

tôi mong đợi việc thực hiện Enumerable.Empty() để được chỉ này:Tại sao Enumerable.Empty() trả về một mảng trống?

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

Nhưng việc thực hiện là một cái gì đó như thế này:

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; 
     } 
    } 
} 

Tại sao việc thực hiện phức tạp hơn chỉ là một dòng mã? Có một lợi thế để trả về một mảng được lưu trong bộ nhớ cache và không (lợi nhuận) trả về không có phần tử nào không?

Lưu ý: Tôi sẽ không bao giờ dựa vào các chi tiết triển khai của một phương pháp, nhưng tôi chỉ tò mò.

+10

'năng suất break' là một dòng trong C#, nhưng nó phức tạp hơn nếu bạn nhìn tại mã IL được tạo ra. – Dirk

Trả lời

6

Biên dịch (sử dụng LINQPad với tối ưu hóa kích hoạt)

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

kết quả ở khá nhiều mã.

Nó sẽ tạo một máy trạng thái thực hiện giao diện IEnumerable. Mỗi khi bạn gọi MyEmpty nó sẽ tạo ra một thể hiện mới của lớp đó. Trả về cùng một thể hiện của một mảng trống khá rẻ.

Mã IL cho EmptyEnumerable là:

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 

Và đối với những phương pháp MyEmpty nó là:

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   
+0

So sánh như thế nào nếu bạn cũng thêm mã IL cần thiết để lưu bộ nhớ đệm? –

+0

@AlexSiepman Khá nhỏ, tôi đã cập nhật câu trả lời với nó. – Dirk

+0

@Dirk Lưu ý quan trọng về điều đó - mặc dù nó tạo ra nhiều IL hơn, đó không phải là lý do tại sao nó không hoạt động tốt như một mảng trống. Sự khác biệt hiệu suất chính là số lượng các đối tượng được tạo ra. Nó không phải là về bao nhiêu mã được tạo ra nhất thiết. –

1

Có nghĩa là làm điều đó vì trong trường hợp đó bạn sẽ là một mảng cho tất cả các phiên bản trống của cùng loại, sẽ yêu cầu ít bộ nhớ hơn. Đó là lý do tại sao cá thể mảng đơn là tĩnh.

Vì không thể thay đổi mảng không có thành phần, nên không thể bị xóa bởi bất kỳ mã nào.

+0

mảng không thay đổi? Kể từ khi? –

+2

Cụ thể là một mảng cho tất cả các phiên bản trống của một loại cụ thể. – juharr

+4

Cụ thể, vì nó là một mảng * trống *, bạn không phải lo lắng về việc bị bẩn, vì không có yếu tố đột biến. Mảng không phải là bất biến, nhưng độ dài của chúng là vì vậy mảng không có phần tử không có hiệu quả bất biến. –

Các vấn đề liên quan