使用Reflection.Emit.ILGenerator::Emit,从第三方.NET库调用方法。

3

我正在尝试创建一个简单的.NET编译器,以教育为目的。在解析、扫描并构建AST之后,我使用Reflection.Emit.ILGenerator来生成.NET程序集。

以下是我用于生成程序集的示例代码:

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类型。最后,我希望能够像使用csc.exe一样将路径引用作为我的Compiler.exe参数。
因此问题是:
  • 如何做到这一点?
  • 所有这些都叫什么名字?(我不知道该搜索什么)
  • 也许我应该使用其他框架来代替Reflection完成所有这些操作?
  • 有其他建议吗...
1个回答

4
这里唯一的变化就是目标方法:
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

  • 如何做到这一点?答:像上面所示。
  • 所有这些被称为什么?答:IL生成、元编程、反射。
  • 也许我应该使用其他框架来代替反射完成所有这些工作?答:反射涵盖了大多数场景;我在一些地方使用IKVM.Reflection.dll,因为它允许我进行跨框架定位,但您可能不需要那个。你可能会发现Sigil很方便,它通过在生成时提供合理的错误报告(而不是在执行时)来消除了 IL 生成过程中不平衡的堆栈等问题。Mono.Cecil 也很有趣。

太酷了!我没想到从dll中获取类型如此简单。谢谢! - kletnoe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接