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 тоже интересно