С# отражение: Если... еще?
В настоящее время у меня возникают проблемы с операторами. Используя следующий код, я хочу сделать вывод, который будет таким же, как при использовании пары 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
и аналогичными инструкциями?
Спасибо.
Ответы
Ответ 1
ILGenerator.ILOffset
дает текущее смещение в потоке IL, если это то, что вы хотите. Вы можете использовать DefineLabel
и MarkLabel
также, как предложил гори.
Единственное различие между brtrue.s
и brtrue
заключается в том, что brtrue.s
- это короткая версия brtrue
. brtrue
использует 4-байтовое смещение, а brtrue.s
использует 1-байтовое смещение. То же самое относится к brfalse
и brfalse.s
(и br
/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 означает "короткая форма", но я не уверен, что это значит.