代码之家  ›  专栏  ›  技术社区  ›  Leahn Novash

在运行时动态创建函数

  •  6
  • Leahn Novash  · 技术社区  · 15 年前

    可能根本不可能做到这一点,但我还是会问。

    实际上,我想做的是能够在运行时重新定义特定类的特定方法。我想写下一个函数,让程序运行并将其附加到委托。可能吗?

    7 回复  |  直到 15 年前
        1
  •  9
  •   Marwie    7 年前

    您有几种方法可以做到这一点:

        2
  •  8
  •   user1228 user1228    15 年前

    最快的(我相信,在3.5中)是创建一个 DynamicMethod . 这也是最可怕的方法。您实际上是在使用IL构建一个方法,它与用机器语言编写代码的感觉大致相同。

    我需要这样做,以便在某些事情中动态附加事件处理程序(好吧,我不需要这样做,我只是想让单元测试事件更容易)。这在当时似乎有点令人畏惧,因为我对IL一无所知,但我想出了一个简单的方法来实现这一点。

    您要做的是创建一个方法,它完全满足您的需要。越紧凑越好。如果我能弄清楚你到底想做什么,我会举个例子。您可以在DLL项目中的类中编写此方法,并在发布模式下编译它。然后在Reflector中打开DLL并反汇编方法。Reflector为您提供了希望反汇编到的语言选项——选择IL。现在,您有了需要添加到动态方法中的确切调用。只需遵循MSDN上的示例,为反射的方法代码切换示例的IL。

    动态方法一经构造,调用速度与编译方法大致相同(我们看过一个测试,其中动态方法可以在大约20毫秒内调用,而反射时间超过200毫秒)。

        3
  •  6
  •   Jon Skeet    15 年前

    您的问题很不清楚,但您当然可以使用表达式树在执行时动态创建委托。(还有其他方法,如CodeDOM,但表达式树更方便 他们做你需要的一切。但是,您可以做的事情有很大的限制。)

    不过,使用lambda表达式和一些捕获的变量通常更容易。

    例如,要创建将指定数量添加到任何整数的函数,可以编写:

    static Func<int, int> CreateAdder(int amountToAdd)
    {
        return x => x + amountToAdd;
    }
    
    ...
    var adder = CreateAdder(10);
    Console.WriteLine(adder(5)); // Prints 15
    

    如果这没有帮助,请澄清你的问题。

        4
  •  4
  •   Jake    15 年前

    并不是说我推荐这个而不是另一个 较好的 选项,但有一个7 th 方法和使用方法 AssemblyBuilder , ModuleBuilder , TypeBuilder MethodBuilder System.Reflection.Emit DynamicMethod .

    例如,您可以使用它们在运行时为类型创建代理类,并覆盖该类型上的虚拟方法。

    让你开始这里是一些代码。。。

    using System;
    using System.Reflection;
    using System.Reflection.Emit;
    
    var myAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(
            new AssemblyName("Test"), AssemblyBuilderAccess.RunAndSave);
    var myModule = myAssembly.DefineDynamicModule("Test.dll");
    var myType = myModule.DefineType("ProxyType", TypeAttributes.Public | TypeAttributes.Class,
                            typeof(TypeToSeverelyModifyInAnUglyWayButItsNecessary));
    var myMethod = myType.DefineMethod("MethodNameToOverride", 
                            MethodAttributes.HideBySig | MethodAttributes.Public,
                            typeof(void),Type.EmptyTypes);
    var myIlGenerator = myMethod.GetILGenerator();
    myIlGenerator.Emit(OpCodes.Ret);
    var type = myType.CreateType();
    
        5
  •  0
  •   Jacob    15 年前

    如果将该方法声明为虚拟方法,则可以使用Castle的 DynamicProxy 要在运行时替换动态生成的(使用另一个答案的方法之一)实现,请执行以下操作:

    Castle DynamicProxy是一个库,用于在运行时动态生成轻量级.NET代理。代理对象允许在不修改类代码的情况下拦截对对象成员的调用。类和接口都可以代理,但是只能拦截虚拟成员。

        6
  •  0
  •   Chris Simpson    15 年前

    你问题的第二段表明你真正想要的是直截了当的 IOC (控制反转)

    您不需要声明类的实例,而是声明接口的实例,并且根据您选择的任何条件,使用具有正确方法的特定重写类。希望这是有道理的。

        7
  •  0
  •   clemahieu    15 年前

    您应该首先检查是否可以使用简单多态性解决您的问题。除非定义与另一种语言的抽象互操作性或编辑编译器,否则在运行时尝试更改方法可能是错误的解决方案。

        8
  •  0
  •   Lei Chi    4 年前

    使用.net core 3.1,您可以使用IL语言创建动态方法(mono也一样):

    using System.Linq.Expressions;
    using System.Reflection.Emit;
    
    class Program
    {
        static void Main()
        {
            DynamicMethod hellomethod = new DynamicMethod("WriteLine", typeof(void), new[] { typeof(string) }, typeof(Program).Module);
            ILGenerator il = hellomethod.GetILGenerator();
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
            il.Emit(OpCodes.Ret);
            Action<string> hello4 = (Action<string>)hellomethod.CreateDelegate(typeof(Action<string>));
            hello4("Hello World!");
        }
    }