Reflection.Emit.ILGenerator::Emit, вызов метода из сторонней библиотеки.net

Я пытаюсь создать простой компилятор.net для образовательных целей. После разбора, сканирования и сборки AST я генерирую сборку.net используя Reflection.Emit.ILGenerator,

Вот мой пример кода для генерации сборки:

static void Main(string[] args)
{
    string moduleName = "Test.exe";

    AssemblyName assemblyName = new AssemblyName(Path.GetFileNameWithoutExtension(moduleName));
    AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Save);
    ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(moduleName);
    TypeBuilder typeBuilder = moduleBuilder.DefineType("Program", TypeAttributes.Public);
    MethodBuilder mainMethod = typeBuilder.DefineMethod(
        "Main", MethodAttributes.Public | MethodAttributes.Static, typeof(void), System.Type.EmptyTypes);


    ILGenerator il = mainMethod.GetILGenerator();

    il.Emit(OpCodes.Ldstr, "Test!");
    il.Emit(OpCodes.Call, typeof(System.Console).GetMethod("WriteLine", new System.Type[] { typeof(string) }));
    il.Emit(OpCodes.Ret);

    typeBuilder.CreateType();
    moduleBuilder.CreateGlobalFunctions();
    assemblyBuilder.SetEntryPoint(mainMethod);
    assemblyBuilder.Save(moduleName);
}

Все отлично работает, этот код генерирует исполняемый файл со следующим классом:

using System;

public class Program
{
    public Program()
    {
    }

    public static void Main()
    {
        Console.WriteLine("Test!");
    }
}

Далее я создаю простую стороннюю библиотеку с одним классом, которая встраивается в ThirdPartyLibrary.dll:

using System;

namespace ThirdPartyLibrary
{
    public class MyPrint
    {
        public static void Print(string s)
        {
            Console.WriteLine("Third party prints: " + s);
        }
    }
}

Теперь я хочу заменить Console.WriteLine вызов метода для MyPrint.Print вызов метода из моей библиотеки и получение кода результата примерно так:

using System;
using ThirdPartyLibrary;

public class Program
{
    public Program()
    {
    }

    public static void Main()
    {
        MyPrint.Print("Test!");
    }
}

Насколько я понимаю, я должен прочитать мой ThirdPartyLibrary.dll файл, затем отразить его как-то, чтобы получить все типы из него, и тогда можно будет использовать MyPrint тип. Наконец, я хочу иметь возможность ссылаться на пути в качестве аргументов myCompiler.exe, например, используя csc.exe.

Итак, вопросы:

  • Как это сделать?
  • Как все это называется? (Я не могу понять, что гуглить)
  • Может быть, я должен использовать некоторые другие фреймворки вместо Reflection, чтобы сделать все это?
  • Любые другие предложения...

1 ответ

Решение

Единственное изменение здесь - целевой метод:

var targetMethod = Assembly.LoadFrom("ThirdPartyLibrary.dll")
    .GetType("ThirdPartyLibrary.MyPrint")
    .GetMethod("Print", new [] {typeof(string)});
...
il.Emit(OpCodes.Ldstr, "Test!");
il.Emit(OpCodes.Call, targetMethod);
il.Emit(OpCodes.Ret);

Так что вместо использования Console.WriteLine, мы называем MyPrint.Print,

  • Как это сделать? A: как указано выше
  • Как все это называется? A: IL-поколение, метапрограммирование, рефлексия
  • Может быть, я должен использовать некоторые другие фреймворки вместо Reflection, чтобы сделать все это? A: отражение охватывает большинство сценариев; я использую IKVM.Reflection.dll в некоторых местах, потому что это позволяет мне выполнять межфреймовый таргетинг, но вам, вероятно, это не нужно. Что вам может пригодиться, так это Sigil, который избавляет от большинства проблем, связанных с генерацией IL (несбалансированные стеки и т. Д.), Предоставляя вам разумные отчеты об ошибках во время создания (а не при выполнении). Mono.Cecil тоже интересно
Другие вопросы по тегам