2014-08-28 5 views
4

Я пытаюсь создать следующий класс:Что случилось с моей простой MSIL?

public class MyType 
{ 
    public string MyMethod() { return "Hi"; } 
} 

Мой Испустите код выглядит следующим образом:

var assemblyBuilder = GetAssemblyBuilder("MyAssembly"); 
var moduleBuilder = assemblyBuilder.DefineDynamicModule("MyModule"); 
var typeBuilder = moduleBuilder.DefineType("MyType", TypeAttributes.Public); 
var methodBuilder = typeBuilder.DefineMethod("MyMethod", MethodAttributes.Public, typeof(string), new Type[] { }); 
var ilBuilder = methodBuilder.GetILGenerator(); 
ilBuilder.Emit(OpCodes.Nop); 
ilBuilder.Emit(OpCodes.Ldstr, "Hi"); 
ilBuilder.Emit(OpCodes.Stloc_0); 
ilBuilder.Emit(OpCodes.Br_S); 
ilBuilder.Emit(OpCodes.Ldloc_0); 
ilBuilder.Emit(OpCodes.Ret); 
var type = typeBuilder.CreateType(); 

но когда я призываю MyMethod на примере MyType я получаю InvalidProgramException: общий язык Runtime обнаружила недопустимую программу.

Я попытался изменить тип возврата на void и используя только EmitWriteLine и Emit(OpCodes.Ret), который работает отлично, поэтому он должен быть IL, который я написал здесь.

Я пропустил что-то очевидное здесь? Ясное объяснение было бы полезно, поскольку я только начинаю с Emit.

Дополнительная информация от комментариев:

"Оригинальный" IL взят из IL-поколения в LINQ-панели.

+0

@ Selman22 Я считаю, что это именно то, что он сделал, это похоже на то, когда скомпилировано IL. – flindeberg

+0

Некоторые советы: 1) такой инструмент, как [Sigil] (https://www.nuget.org/packages/Sigil/), будет намного сложнее ошибиться; 2) если вы не уверены, скомпилируйте то, что вы ** хотите делать как обычный C# -код, затем перепрограммируйте IL в ILDASM или отражателе или аналогичном –

+1

3) «nop» в IL - верный признак того, что вы на самом деле сделал # 2, но посмотрел на вывод отладочной сборки. Не делай этого; при выполнении # 2 всегда используйте сборку релизов. –

ответ

5

В дополнение к устранению OpCodes.Br_S как указал @Kendall, вам нужно объявить локальную переменную вы хранящий "Hi" к:

ilBuilder.DeclareLocal(typeof(string)); 
ilBuilder.Emit(OpCodes.Nop); 
ilBuilder.Emit(OpCodes.Ldstr, "Hi"); 
ilBuilder.Emit(OpCodes.Stloc_0); 
ilBuilder.Emit(OpCodes.Ldloc_0); 
ilBuilder.Emit(OpCodes.Ret); 

Кроме того, я думаю, что все это может быть сокращено до (это является то, что вы получите, если вы строите свой класс с оптимизацией компилятора на):

ilBuilder.Emit(OpCodes.Ldstr, "Hi"); 
ilBuilder.Emit(OpCodes.Ret); 

(Просто загрузите строку, поставив его на вершине evaluat ионный стек, а затем верните строку).

Если вы хотите скопировать версию с компилятором оптимизаций офф, которые я думаю, это то, что вы изначально пытаются сделать:

Label returnLabel = ilBuilder.DefineLabel(); 

ilBuilder.DeclareLocal(typeof(string)); 
ilBuilder.Emit(OpCodes.Nop); 
/* Load the string "HI" and put it on the evaluation stack. */ 
ilBuilder.Emit(OpCodes.Ldstr, "Hi"); 

/* Store "Hi" in the local variable we declared. */ 
ilBuilder.Emit(OpCodes.Stloc_0); 

/* Jump to the return label, which is, err the next line anyway: */ 
ilBuilder.Emit(OpCodes.Br_S, returnLabel); 

/* Mark "returnLabel" here so we can jump to it: */ 
ilBuilder.MarkLabel(returnLabel);  

/* Load the contents of our local variable, put it on the evaluation stack: */ 
ilBuilder.Emit(OpCodes.Ldloc_0); 

/* Return "Hi", which is now back on top of the evaluation stack: */ 
ilBuilder.Emit(OpCodes.Ret); 

Вы должны определить метку перейти к (с использованием ILGenerator.DefineLabel, использовать MarkLabel, а затем передать эту метку Emit при испускает OpCodes.Br_S.

Как вы можете видеть, хотя, разветвление рода бессмысленно с методом этой коротким.

+0

Вы абсолютно правы в сокращенной версии. –

+0

Что такое эквивалент в созданном IL-коде 'DeclareLocal', поскольку я не вижу ничего подобного ... –

+0

@ dav_i: Вы используете LINQPad? Если это так, вкладка «IL» этой программы не покажет вам объявленных локалей. –

3
ilBuilder.Emit(OpCodes.Br_S); 

Куда ты собирался вступить? br.s требует подписанный аргумент байта, который дает смещение для перехода.

+0

Спасибо Kendall, я просто копировал сгенерированный IL из класса класса, который я сделал ... и пропустил это, очевидно. Как вы определяете, где 'br.s', поскольку сгенерированный код определяет« номера строк ». –

+0

Вы можете передать ему ярлык, используя ['Emit (OpCodes.Br_S, theLabel)'] (http://msdn.microsoft.com/en-us/library/ahkzc1bh.aspx). См. ['Label'] (http://msdn.microsoft.com/en-us/library/system.reflection.emit.label.aspx) для образцов кода о том, как их использовать. –

+0

@ dav_i вы обычно не делаете; вы используете 'DefineLabel' и переходите в ярлык - помните также, чтобы использовать« MarkLabel »в точке приземления. –

Смежные вопросы