Подтвердить что ты не робот

С# отражение: Если... еще?

В настоящее время у меня возникают проблемы с операторами. Используя следующий код, я хочу сделать вывод, который будет таким же, как при использовании пары if ... else в С#.

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();

Мои вопросы:

  • Как я могу получить адрес инструкции для передачи ее в качестве параметра для кодов операций ветки?
  • Есть ли разница между BR и BR_S, Brtrue и Brtrue_S, Brfalse и Brfalse_S и аналогичными инструкциями?

Спасибо.

4b9b3361

Ответ 1

ILGenerator.ILOffset дает текущее смещение в потоке IL, если это то, что вы хотите. Вы можете использовать DefineLabel и MarkLabel также, как предложил гори.

Единственное различие между brtrue.s и brtrue заключается в том, что brtrue.s - это короткая версия brtrue. brtrue использует 4-байтовое смещение, а brtrue.s использует 1-байтовое смещение. То же самое относится к brfalse и brfalse.sbr/br.s).

Это не единственные короткие версии команды IL, есть и другие короткие инструкции, например ldc.i4.0 - ldc.i4.8 для загрузки целых чисел. Они в основном полезны для создания небольших двоичных файлов, но я не думаю, что в противном случае есть большая разница.

Ответ 2

  • Вы можете использовать комбинацию DefineLabel и MarkLabel, чтобы определить целевое местоположение филиал. Чтобы сделать это, объявите ярлыки, которые вам понадобятся - что-то вроде equal и notequal. Затем вы можете отметить пятна в вашем IL, где метки должны присутствовать. Как только это будет сделано, вы можете установить target вашей инструкции перехода на эту метку.

    // 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);
    
  • Разница между Br, Brtrue и Brfalse и их _S контрагенты - это длина прыжка. _S обозначает короткую форму; целевая инструкция представляет собой 1-байтовое смещенное смещение от следующей команды. В стандартной (не короткой) форме целевым является представленный 4-байтным смещением.

Ответ 3

Это ново для меня, но из небольшого поиска, видимо, вы получаете адрес, вызывая

Label targetInstruction = g.DefineLabel();

в какой-то момент (например, перед вашим первым Emit?), а затем

g.MarkLabel(targetInstruction);

перед тем, как исправить строку, на которую вы хотите перейти. Затем этот Label является аргументом вашего кода Br____.

Разница между Br, Brtrue и Brfalse заключается в том, что Br всегда веткится, а остальные два выставляют значение из стека и только ветвь, если она истинна или ложна соответственно, (Так что да, есть большая разница!) _S означает "короткая форма", но я не уверен, что это значит.