【问题标题】:Creating a function dynamically at run-time在运行时动态创建函数
【发布时间】:2009-07-03 16:23:29
【问题描述】:

可能甚至不可能做到这一点,但我还是会问。 是否可以创建一个接收字符串的函数,然后将其用作 lambda 中使用的转到运算符 (=>) 的右侧参数?

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

【问题讨论】:

  • 您能告诉我们您的实际用例是什么吗?可能有其他方法可以满足您的要求。
  • 如果我没看错,你想 eval() 一个字符串吗? en.wikipedia.org/wiki/Eval

标签: c# .net-3.5 delegates lambda


【解决方案1】:

你有几种方法可以做到:

【讨论】:

  • 我希望我可以选择两个答案作为正确的答案。两者都为我提供了非常好的解决方案。在这里,我认为这是不可能的。现在,策略模式不是我想要的。要使用 Strategy,我必须实现这些方法,以便能够交换它们。
【解决方案2】:

最简单的方法可能是 TcKs 建议的 LINQ。

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

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

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

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

【讨论】:

  • 哇,在这里我以为我对 .NET 有所了解。我从来没有听说过。我不敢问你是怎么偶然发现这样的功能的。
  • DynamicMethod 自 2.0 起可用
【解决方案3】:

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

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

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

static Func<int, int> CreateAdder(int amountToAdd)
{
    return x => x + amountToAdd;
}

...
var adder = CreateAdder(10);
Console.WriteLine(adder(5)); // Prints 15

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

【讨论】:

    【解决方案4】:

    并不是说我推荐这个优于其他更好选项,但有第 7th 方法,那就是使用AssemblyBuilder,ModuleBuilder,@987654323 @ 和 System.Reflection.Emit 命名空间中的 MethodBuilder 以创建动态程序集。这与使用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();
    

    【讨论】:

    • 与简单地使用 DynamicMethod 相比,它似乎需要大约相同数量的代码来完成。有什么理由在 DynamicMethod 上使用它吗?如果 Will 是对的,DynamicMethods 几乎与编译代码一样快(尽管 20 毫秒在计算机时间中是永恒的)。
    • 使用 this 优于动态方法的唯一真正“优势”是您可以保存生成的程序集,并且可以从类型继承并覆盖虚拟方法/属性。希望这可以帮助。同样,我不建议将其用于一般消费,只是指出另一种选择。当你说你需要“重新定义”一个方法时,这取决于你的意思,这可能是一个更好的选择,因为你可以创建一个类型并覆盖基本方法......
    【解决方案5】:

    使用.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!");
        }
    }
    

    【讨论】:

      【解决方案6】:

      如果您将该方法声明为虚拟方法,则可以使用 Castle 的 DynamicProxy 在运行时替换动态生成的(使用其他答案的方法之一)实现:

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

      【讨论】:

      • 我倾向于避免在我的代码中使用第三方库。这是为了避免供应商锁定问题。我以前在 Delphi 中编码时遇到过严重的问题。例如,我刚刚从使用 SQLite(尽管我非常喜欢它)切换到 SQLServer CE,只是为了完全留在框架内。
      • 它确实具有开源的优势,因此您不会像使用商业解决方案那样“锁定”。有时您只需要权衡重新发明轮子与实际完成任务的优势。
      【解决方案7】:

      您问题的第二段表明,您可能真正追求的是直截了当的IOC(控制反转)

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

      【讨论】:

      • 不,不是。我想要的是能够在运行时将类的方法修改为我将在运行时键入的内容。与 eval 非常相似,只是我不只是希望它 eval 并死掉。在我需要再次更改它之前,我希望它一直依附于委托。
      • 啊,好的,所以你的应用可能有一个文本输入,可以让你输入一些代码来修改函数的操作?
      【解决方案8】:

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

      【讨论】:

      • 我同意 100%,这应该是首选。
      猜你喜欢
      • 2014-03-15
      • 2012-07-02
      • 1970-01-01
      • 2021-06-27
      • 2012-06-16
      • 2020-05-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多