2011-07-28 35 views
17

Rõ ràng, bảo đảm khu vực thực thi hạn chế không áp dụng cho các trình vòng lặp (có thể là do chúng được thực hiện và tất cả), nhưng đây có phải là lỗi hoặc theo thiết kế không? [Xem ví dụ bên dưới.]Làm C# cố gắng cuối cùng CERs phá vỡ trong vòng lặp?

tức là các quy tắc về CER được sử dụng với trình vòng lặp là gì?

using System.Runtime.CompilerServices; 
using System.Runtime.ConstrainedExecution; 

class Program 
{ 
    static bool cerWorked; 
    static void Main(string[] args) 
    { 
     try 
     { 
      cerWorked = true; 
      foreach (var v in Iterate()) { } 
     } 
     catch { System.Console.WriteLine(cerWorked); } 
     System.Console.ReadKey(); 
    } 

    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
    unsafe static void StackOverflow() 
    { 
     Big big; 
     big.Bytes[int.MaxValue - 1] = 1; 
    } 

    static System.Collections.Generic.IEnumerable<int> Iterate() 
    { 
     RuntimeHelpers.PrepareConstrainedRegions(); 
     try { cerWorked = false; yield return 5; } 
     finally { StackOverflow(); } 
    } 

    unsafe struct Big { public fixed byte Bytes[int.MaxValue]; } 
} 

(Mã chủ yếu là bị đánh cắp từ here.)

+2

Đối với những gì nó có giá trị bạn có vẻ là người đầu tiên nhận thấy điều này ... ít nhất là như xa như tôi có thể nói từ googling cho các tài liệu tham khảo khác của nó. –

+0

Tôi đã tìm thấy đoạn mã https://vmccontroller.svn.codeplex.com/svn/VmcController/VmcServices/DetectOpenFiles.cs này trong đó tác giả không ngờ sẽ không nhận được CER mà anh ta nghĩ là anh ta đang nhận được. –

+0

@Brian: Rất tiếc. Tôi nghĩ rằng đó là điều mà hầu hết mọi người không thường xuyên sử dụng, và những người có thể đã biết trực giác, mà thực sự không bao giờ nghĩ về nó. Chỉ là tôi đoán thôi. – Mehrdad

Trả lời

14

Vâng, tôi không biết nếu điều này là một lỗi hay chỉ là một thực sự trường hợp cạnh lạ trong đó CERs không được thiết kế để xử lý.

Vì vậy, đây là mã thích hợp.

private static IEnumerable<int> Iterate() 
{ 
    RuntimeHelpers.PrepareConstrainedRegions(); 
    try { cerWorked = false; yield return 5; } 
    finally { StackOverflow(); } 
} 

Khi điều này được biên soạn và chúng tôi cố gắng dịch ngược lại thành C# với Trình phản xạ, chúng tôi nhận được điều này.

private static IEnumerable<int> Iterate() 
{ 
    RuntimeHelpers.PrepareConstrainedRegions(); 
    cerWorked = false; 
    yield return 5; 
} 

Bây giờ, hãy chờ một chút! Reflector có tất cả điều này hơi say lên. Đây là những gì IL thực sự trông giống như.

.method private hidebysig static class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> Iterate() cil managed 
{ 
    .maxstack 2 
    .locals init (
     [0] class Sandbox.Program/<Iterate>d__1 d__, 
     [1] class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> enumerable) 
    L_0000: ldc.i4.s -2 
    L_0002: newobj instance void Sandbox.Program/<Iterate>d__1::.ctor(int32) 
    L_0007: stloc.0 
    L_0008: ldloc.0 
    L_0009: stloc.1 
    L_000a: br.s L_000c 
    L_000c: ldloc.1 
    L_000d: ret 
} 

Lưu ý rằng trên thực tế, không gọi tới PrepareConstrainedRegions bất chấp Reflector nói gì. Vậy nó ở đâu? Vâng, nó ở ngay trong phương pháp MoveNext được tạo tự động. Lần này Reflector hiểu đúng.

private bool MoveNext() 
{ 
    try 
    { 
     switch (this.<>1__state) 
     { 
      case 0: 
       this.<>1__state = -1; 
       RuntimeHelpers.PrepareConstrainedRegions(); 
       this.<>1__state = 1; 
       Program.cerWorked = false; 
       this.<>2__current = 5; 
       this.<>1__state = 2; 
       return true; 

      case 2: 
       this.<>1__state = 1; 
       this.<>m__Finally2(); 
       break; 
     } 
     return false; 
    } 
    fault 
    { 
     this.System.IDisposable.Dispose(); 
    } 
} 

Và cuộc gọi đó đến đâu để chuyển sang chế độ StackOverflow? Ngay bên trong phương thức m_Finally2().

private void <>m__Finally2() 
{ 
    this.<>1__state = -1; 
    Program.StackOverflow(); 
} 

Vì vậy, cho phép kiểm tra này một chút chặt chẽ hơn. Hiện tại, chúng tôi có số cuộc gọi PrepareConstainedRegions bên trong khối try thay vì ở bên ngoài vị trí cần thiết. Và cuộc gọi StackOverflow của chúng tôi đã di chuyển từ một khối finally đến khối try.

Theo số documentationPrepareConstrainedRegions phải lập tức đứng trước khối try. Vì vậy, giả định là nó không có hiệu quả nếu được đặt ở bất kỳ nơi nào khác.

Nhưng, ngay cả khi trình biên dịch C# nhận được phần đúng, mọi thứ sẽ vẫn bị vặn lên vì các khối try không bị ràng buộc. Chỉ có các khối catch, finallyfault. Và đoán xem? Cuộc gọi StackOverflow đã được di chuyển từ khối finally tới khối try!

+0

+1, câu trả lời hay, nhưng khối 'lỗi' là gì? ** Chỉnh sửa: ** không bao giờ bận tâm nó là một cái gì đó liên quan đến [năng suất] (http://startbigthinksmall.wordpress.com/2008/06/09/behind-the-scenes-of-the-c-yield-keyword/) –

+1

@Jalal: Không, đó là * không * liên quan đến lợi nhuận. Nó khá nhiều chỉ là 'bắt {... ném; } ', không có câu lệnh bổ sung' throw'. (Vì chúng khá giống nhau, nó không phải là một tính năng của C#.) – Mehrdad

+1

@Jalal Một khối lỗi giống như một khối cuối cùng, nhưng chỉ chạy khi kiểm soát lá do ngoại lệ. [Đây là một chút nữa về điều đó.] (Http://www.simple-talk.com/community/blogs/simonc/archive/2011/02/09/99250.aspx) Trình biên dịch _uses it_ trong việc thực hiện một máy trạng thái có thể đếm được, nhưng nó không dành riêng cho từ khóa lợi nhuận. –

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