Наконец, мы генерируем блок catch и завершаем генерацию нашей программы:
il.BeginCatchBlock (typeof (System.Exception)); il.EmitWriteLine ("Finished"); il.EndExceptionBlock (); il.Emit (OpCodes.Ret); classb.CreateType (); ab.SetEntryPoint (mb); ab.Save ("numbers.exe");
Теперь мы перейдем к генерации catch-блока. Стоит отметить следующую деталь: если бы try не заканчивался переходом к следующей итерации, то метод BeginCatchBlock автоматически сгенерировал бы команду для завершения блока.
il.BeginCatchBlock (typeof (System.Exception));
Метод EmitWriteLine используется для генерации вызова System.Console.WriteLine от строковой константы. Тот же эффект можно достичь и более прямым путем (так же, как мы выше выводили значение переменной var_i). Результат будет идентичен, но использованный нами только что способ более краток и удобен, например, для вставки в код вывода отладочной информации.
il.EmitWriteLine ("Finished");
Метод EndExceptionBlock завершает генерацию try-catch блока. Он одновременно выполняет определение метки EndTry, которую создал BeginExceptionBlock. После окончания этого блока необходимо вставить генерацию оператора return.
il.EndExceptionBlock (); il.Emit (OpCodes.Ret);
Теперь нам осталось выполнить только несколько завершающих действий: операция CreateType завершает создание типа. После этого мы уже не можем внести никаких изменений и дополнений в класс LowLevelSample.
classb.CreateType();
Метод SetEntryPoint позволяет установить точку входа для сборки (на уровне генерации MSIL нет никаких неявных соглашений на эту тему, как, скажем, в C#, где точкой входа является метод Main):
ab.SetEntryPoint (mb); ab.Save ("numbers.exe");
И вот выполнено последнее действие: после операции Save сборка записана в исполняемый файл на диске. Процесс генерации кода завершен.