12

Tôi hiện đang gặp phải sự cố mới với các nhà khai thác. Sử dụng mã sau đây, tôi muốn tạo đầu ra giống như khi sử dụng cặp if ... else trong C#.Phản ánh C#: Nếu ... khác?

var method = new DynamicMethod("dummy", null, Type.EmptyTypes); 
var g = method.GetILGenerator(); 

g.Emit(OpCodes.Ldstr, "string"); 
g.Emit(OpCodes.Ldstr, "string"); 
g.Emit(OpCodes.Call, typeof(String).GetMethod("op_Equality", new Type[]{typeof(string), typeof(string)})); 
g.Emit(OpCodes.Ldc_I4, 0); 
g.Emit(OpCodes.Ceq); 
g.Emit(OpCodes.Brtrue_S,); 

var action = (Action)method.CreateDelegate(typeof(Action)); 
action(); 

Console.Read(); 

Câu hỏi của tôi là:

  1. Làm thế nào tôi có thể nhận được địa chỉ của một hướng dẫn để vượt qua nó như một tham số cho các opcodes chi nhánh?
  2. Có sự khác biệt giữa BRBR_S, BrtrueBrtrue_S, BrfalseBrfalse_S và hướng dẫn tương tự?

Cảm ơn.

+3

Như những người khác đã đề cập, các phiên bản "_S" của các hướng dẫn chi nhánh lấy chênh lệch 1 byte thay vì 4 byte. Nếu bạn biết rằng nhánh của bạn sẽ luôn nằm trong phạm vi sẵn có (-128 byte đến +127 byte), thì bạn sẽ nhận được mã nhỏ gọn hơn bằng cách sử dụng chúng, tuy nhiên nếu bạn cố gắng sử dụng chúng để chuyển đến nhãn có bù đắp bên ngoài phạm vi này, một ngoại lệ sẽ được ném khi tạo đại biểu. – Iridium

Trả lời

8

ILGenerator.ILOffset cung cấp cho bạn mức bù hiện tại trong luồng IL, nếu đó là những gì bạn muốn. Bạn cũng có thể sử dụng DefineLabelMarkLabel, như goric được đề xuất.

Sự khác biệt duy nhất giữa brtrue.sbrtruebrtrue.s là phiên bản ngắn brtrue. brtrue sử dụng bù đắp 4 byte và brtrue.s sử dụng bù đắp 1 byte. Điều tương tự cũng áp dụng cho brfalsebrfalse.s (và br/br.s).

Đó không phải là phiên bản ngắn chỉ có một hướng dẫn IL, cũng có các hướng dẫn ngắn khác, như ldc.i4.0 - ldc.i4.8 để tải các số nguyên. Đó là chủ yếu hữu ích cho việc tạo ra các tệp nhị phân nhỏ hơn, nhưng tôi không nghĩ rằng có một sự khác biệt lớn khác.

8
  1. Bạn có thể sử dụng kết hợp các phương pháp DefineLabelMarkLabel để xác định vị trí mục tiêu của chi nhánh . Để làm như vậy, hãy khai báo các nhãn bạn cần - một cái gì đó như equalnotequal. Sau đó, bạn có thể đánh dấu các điểm trong IL của bạn, nơi các nhãn nên có mặt. Khi việc này hoàn tất, bạn có thể đặt mục tiêu của hướng dẫn chi nhánh của mình cho nhãn này.

    // Define labels 
    Label equal = g.DefineLabel(); 
    Label notEqual = g.DefineLabel(); 
    Label endOfMethod = g.DefineLabel(); 
    // your logic here 
    g.Emit(OpCodes.Brtrue_S, equal); 
    g.MarkLabel(equal); 
    // some code if they are equal 
    g.MarkLabel(notEqual); 
    // some code if they are not equal 
    g.MarkLabel(endOfMethod); // this marks the return point 
    g.Emit(OpCodes.Ret); 
    
  2. Sự khác biệt giữa Br, Brtrue, và Brfalse và họ _S đối tác là chiều dài của các bước nhảy. _S biểu thị dạng ngắn; hướng dẫn đích là một ký hiệu bù đắp 1 byte từ lệnh kế tiếp. Trong biểu mẫu chuẩn (không ngắn), mục tiêu là được biểu thị bằng độ lệch 4 byte.

4

Đây là mới với tôi, nhưng từ một ít tìm kiếm rõ ràng bạn sẽ có được địa chỉ bằng cách gọi

Label targetInstruction = g.DefineLabel(); 

tại một số điểm trước (ví dụ trước đầu tiên Emit của bạn?), Và sau đó

g.MarkLabel(targetInstruction); 

ngay trước phát ra dòng bạn muốn chi nhánh -. Sau đó, Label này là đối số cho mã vạch Br____ của bạn.

Sự khác biệt giữa Br, BrtrueBrfalse là Br luôn là nhánh và cửa sổ thứ hai bật giá trị ra khỏi ngăn xếp và chỉ phân nhánh nếu giá trị đúng hoặc sai tương ứng. (Vì vậy, có, có một sự khác biệt lớn!) _S biểu thị "dạng ngắn" nhưng tôi không chắc điều này có nghĩa là gì.