Импорт метода завершается ошибкой с помощью Mono.Cecil

У меня есть исходный код класса TestClass, хранящийся в виде строки, и мне нужно создать DLL с другим классом, содержащим код из одного метода TestClass. Вот TestClass, хранящийся в виде строки:

namespace Test
{
    public static class TestClass
    {
        public static void TestMethod()
        {
            System.Console.WriteLine("something");
        }
    }
}

Я компилирую этот код, используя CSharpCodeProvider, затем с помощью Mono.Cecil создаю новую сборку, создаю новый тип, новый метод и копирую (фактически клонирую) в него инструкции скомпилированного метода. Что не работает, так это на этапе записи сборки на диск, выдается исключение:

Необработанное исключение типа "System.ArgumentException" возникло в Mono.Cecil.dll Дополнительная информация: Элемент "System.Void System.Console::WriteLine(System.String)" объявлен в другом модуле и должен быть импортирован.

Метод Console.WriteLine импортируется, как указано здесь в документации

Вот полный код:

class Program
{
    static void Main()
    {
        var assemblyDefinition = AssemblyDefinition.CreateAssembly(
            new AssemblyNameDefinition("Test", new Version(1, 0)), "module", ModuleKind.Console);
        var type = new TypeDefinition("Test", "TestClass", TypeAttributes.Public);
        var methodDefinition = new MethodDefinition(
            "TestMethod",
            MethodAttributes.Static | MethodAttributes.Public,
            assemblyDefinition.MainModule.Import(typeof(void)));
        type.Methods.Add(methodDefinition);
        assemblyDefinition.MainModule.Types.Add(typeDefinition);
        AddMethodBody(methodDefinition);
        assemblyDefinition.Write("generated.dll");
    }

    private static void AddMethodBody(MethodDefinition methodDefinition)
    {
        var parameters = new CompilerParameters();
        parameters.ReferencedAssemblies.AddRange(AppDomain.CurrentDomain.GetAssemblies().Select(x => x.Location).ToArray());
        var result = _compiler.CompileAssemblyFromSource(parameters, TestClassSource);

        var compiledAssembly = AssemblyDefinition.ReadAssemblly(result.CompiledAssembly.Location);
        var type = compiledAssembly.MainModule.Types.First(x => x.FullName == "Test.TestClass");
        var method = type.Methods.First(x => x.Name == "TestMethod");

        var instructions = method.Body.Instructions;

        var ctorInfo = typeof(Instruction).GetConstructor(
            BindingFlags.NonPublic | BindingFlags.Instance, null,
            new[] { typeof(OpCode, typeof(object) }, null);

        foreach(var inst in instructions)
        {
            var newInstruction = (Instruction)ctorInfo.Invoke(new[] {inst.OpCode, inst.Operand});
            methodDefinition.Body.Instructions.Add(newInstruction);
        }

        var il = methodDefinition.Body.GetILProcessor();
        var ldstr = il.Create(OpCodes.Ldstr, methodDefinition.Name);
        var class = il.Create(OpCodes.Call,
            methodDefinition.Module.Import(typeof(Console).GetMethod("WriteLine", new[] { typeof(string) })));

        il.InsertBefore(methodDefinition.Body.Instructions[0], ldstr);
        il.InsertAfter(methodDefinition.Body.Instructions[0], call);
    }

    private static string TestClassSource = 
@"
namespace Test
{
    public static class TestClass
    {
        public static void TestMethod()
        {
            System.Console.WriteLine(""abc"");
        }
    }
}"

}

Любая помощь будет оценена

0 ответов

Другие вопросы по тегам