2012-03-09 32 views
10

Đầu tiên, một số thông tin cơ bản:Reflection.Emit.ILGenerator Xử lý ngoại lệ Lệnh "Leave"

Tôi đang làm trình biên dịch cho một dự án trường học. Nó đã được làm việc, và tôi đang nỗ lực hết sức để sửa lỗi và/hoặc tối ưu hóa nó. Gần đây tôi đã chạy vào một vấn đề với là tôi phát hiện ra rằng các đối tượng ILGenerator tạo thêm leave hướng dẫn khi bạn gọi bất kỳ phương pháp thành viên sau đây:

BeginCatchBlock() 
BeginExceptFilterBlock() 
BeginFaultBlock() 
BeginFinallyBlock() 
EndExceptionBlock() 

Vì vậy, bạn bắt đầu một tuyên bố thử với một cuộc gọi đến BeginExceptionBlock(), thêm một vài mệnh đề bắt với BeginCatchBlock(), có thể thêm mệnh đề cuối cùng với BeginFinallyBlock() và sau đó kết thúc khu vực mã được bảo vệ bằng EndExceptionBlock().

Các phương pháp tôi liệt kê sẽ tự động tạo ra một phân nhánh lệnh leave hướng dẫn đầu tiên sau câu lệnh thử. Tôi không muốn điều này, vì hai lý do. Một, bởi vì nó luôn tạo ra một hướng dẫn leave chưa được tối ưu hóa, thay vì hướng dẫn leave.s, ngay cả khi chỉ phân nhánh hai byte. Và hai, bởi vì bạn không thể kiểm soát nơi mà lệnh rời đi. Vì vậy, nếu bạn muốn chi nhánh đến một số vị trí khác trong mã của bạn, bạn phải thêm biến cục bộ do trình biên dịch tạo, đặt tùy thuộc vào nơi bạn muốn đi vào bên trong câu lệnh try, hãy tự động tạo ra EndExceptionBlock() lệnh leave và sau đó tạo câu lệnh chuyển đổi bên dưới khối thử. OR, bạn chỉ có thể phát ra một leave hoặc leave.s hướng dẫn cho mình, trước khi gọi một trong những phương pháp trước đây, dẫn đến một xấu xí và không thể truy cập thêm 5 byte, như vậy:

L_00ca: leave.s L_00e5 
L_00cc: leave L_00d1 

Cả hai tùy chọn là không thể chấp nhận đối với tôi. Có cách nào để ngăn chặn việc tạo tự động các hướng dẫn leave hay bất kỳ cách nào khác để chỉ định các khu vực được bảo vệ thay vì sử dụng các phương pháp này (điều này cực kỳ khó chịu và thực tế không có giấy tờ)?

EDIT Lưu ý: trình biên dịch C# tự thực hiện việc này, vì vậy không phải là có lý do chính đáng để ép buộc chúng tôi. Ví dụ, nếu bạn có .NET 4.5 beta, tháo rời các mã sau và kiểm tra việc thực hiện: (khối ngoại lệ bổ sung trong nội bộ)

public static async Task<bool> TestAsync(int ms) 
{ 
    var local = ms/1000; 
    Console.WriteLine("In async call, before await " + local.ToString() + "-second delay."); 
    await System.Threading.Tasks.Task.Delay(ms); 
    Console.WriteLine("In async call, after await " + local.ToString() + "-second delay."); 

    Console.WriteLine(); 
    Console.WriteLine("Press any key to continue."); 
    Console.ReadKey(false); 
    return true; 
} 

Trả lời

7

Theo như tôi có thể nói, bạn không thể làm điều này trong .NET 4.0. Cách duy nhất để tạo thân phương thức mà không cần sử dụng ILGenerator bằng cách sử dụng MethodBuilder.CreateMethodBody, nhưng điều đó không cho phép bạn đặt thông tin xử lý ngoại lệ. Và ILGenerator bắt buộc hướng dẫn leave bạn đang hỏi.

Tuy nhiên, nếu .NET 4.5 là tùy chọn cho bạn (có vẻ như), hãy xem MethodBuilder.SetMethodBody. Điều này cho phép bạn tự tạo IL, nhưng vẫn đi qua thông tin xử lý ngoại lệ. Bạn có thể quấn lớp này theo một lớp tùy chỉnh ILGenerator giống như của riêng bạn, với các phương pháp Emit lấy một đối số OpCode và đọc OpCode.SizeOpCode.Value để nhận các byte tương ứng.

Và dĩ nhiên luôn có Mono.Cecil, nhưng điều đó có thể yêu cầu những thay đổi lớn hơn đối với mã bạn đã viết.

Chỉnh sửa: you appear to have already figured this out yourself, nhưng bạn đã mở câu hỏi này. Bạn có thể đăng câu trả lời cho câu hỏi của riêng bạn và chấp nhận chúng, nếu bạn đã tự mình tìm ra câu trả lời. Điều này sẽ cho tôi biết rằng tôi không nên lãng phí thời gian tìm kiếm, và điều đó sẽ khiến những người khác có cùng một câu hỏi biết phải làm gì.

+0

Cảm ơn câu trả lời. Tôi để nó mở vì tôi đã không chắc chắn nếu các giải pháp tôi tìm thấy (MethodBuilder.SetMethodBody) thực sự sẽ làm việc cho tôi - nó không có một tài liệu mô tả rất. Cảm ơn một lần nữa! – aboveyou00