通过接口动态创建类

2023-12-29

我有一些经验.Net Expressions,当我能够动态生成方法时。没关系,很好。

但现在我需要生成一个完整的类,似乎唯一的方法就是发出整个IL,这是完全不可接受的(不可能支持)。

假设我们有以下接口:

public interface IFoo
{
    [Description("5")]
    int Bar();
    [Description("true")]
    bool Baz();
}

应该转换为:

public class Foo : IFoo
{
    public int Bar() => 5;
    public bool Baz() => true;
}

我怎样才能实现它?没有第三方工具和库是否有可能?我知道 GitHub 上有很多有用的实用工具,但我真的不想导入整个 MVVM 框架来生成一些代码。

如果我可以使用Expressions,并使用我已经生成的方法创建一个类。但现在我不知道该怎么做。


首先,由于您正在处理远程处理,所以我必须提到,.NET 最初是从头开始设计支持这种功能的(从 .NET 的根源 COM 2.0 开始)。您最直接的解决方案是实现一个透明的远程代理 - 只需创建您自己的(可能是通用的)类派生自System.Runtime.Remoting.Proxies.RealProxy,并且您可以通过覆盖来提供实现您需要的任何功能所需的所有逻辑Invoke方法。使用GetTransparentProxy,您就得到了实现您的接口的代理,然后就可以开始了。

显然,这在运行时、每次调用期间都会产生成本。然而,与进行任何 I/O 相比,这通常完全不重要,尤其是在处理网络时。事实上,除非你处于一个紧密的循环中,否则即使不执行 I/O,它也非常不重要 - 只有性能测试才能真正告诉你是否可以接受成本。

如果您确实想预先生成所有方法体,而不是在运行时保持逻辑动态,您可以利用以下事实:LambdaExpression给你CompileToMethod。不像Compile,您没有得到一个可以直接调用的漂亮的小委托,但它为您提供了使用 lambda 表达式显式构建方法体的选项 - 这反过来又允许您创建整个类,而无需诉诸委托调用。

一个完整(但简单)的例子:

void Main()
{
  var ab = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("TestAssembly"), AssemblyBuilderAccess.Run);
  var mb = ab.DefineDynamicModule("Test");

  var tb = mb.DefineType("Foo");
  tb.AddInterfaceImplementation(typeof(IFoo));

  foreach (var imethod in typeof(IFoo).GetMethods())
  {
    var valueString = ((DescriptionAttribute)imethod.GetCustomAttribute(typeof(DescriptionAttribute))).Description;

    var method = 
      tb.DefineMethod
      (
        "@@" + imethod.Name, 
        MethodAttributes.Private | MethodAttributes.Static, 
        imethod.ReturnType,
        new [] { tb }
      );

    // Needless to say, I'm making a lot of assumptions here :)
    var thisParameter = Expression.Parameter(typeof(IFoo), "this");

    var bodyExpression =
      Expression.Lambda
      (
        Expression.Constant
        (
          Convert.ChangeType(valueString, imethod.ReturnType)
        ),
        thisParameter
      );

    bodyExpression.CompileToMethod(method);

    var stub =
      tb.DefineMethod(imethod.Name, MethodAttributes.Public | MethodAttributes.Virtual, imethod.ReturnType, new Type[0]);

    var il = stub.GetILGenerator();
    il.Emit(OpCodes.Ldarg_0);
    il.EmitCall(OpCodes.Call, method, null);
    il.Emit(OpCodes.Ret);

    tb.DefineMethodOverride(stub, imethod);
  }

  var fooType = tb.CreateType();
  var ifoo = (IFoo)Activator.CreateInstance(fooType);

  Console.WriteLine(ifoo.Bar()); // 5
  Console.WriteLine(ifoo.Baz()); // True
}

public interface IFoo
{
    [Description("5")]
    int Bar();
    [Description("true")]
    bool Baz();
}

如果您曾经使用过 .NET 发出,那么这应该非常简单。我们定义动态程序集、模块、类型(理想情况下,您希望在单个动态程序集中一次定义所有类型)。棘手的部分是Lambda.CompileToMethod只支持静态方法,所以我们需要作一点欺骗。首先,我们创建一个静态方法,它需要this作为参数并在那里编译 lamdba 表达式。然后,我们创建一个方法存根 - 一段简单的 IL,确保正确调用我们的静态方法。最后,我们将接口方法绑定到存根。

在我的示例中,我假设采用无参数方法,但只要您确保LambdaExpression使用与接口方法完全相同的类型,存根就像执行所有操作一样简单Ldargs 在一个序列中,单个Call和一个单一的Ret。如果您的实际代码(在静态方法中)足够短,它通常会被内联。自从this是一个像任何其他参数一样的参数,如果您喜欢冒险,您可以只获取生成方法的方法主体并将其直接放入虚拟方法中 - 但请注意,您需要分两次执行此操作。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

通过接口动态创建类 的相关文章

随机推荐

  • 如何在 API 级别 < 28 上获取主线程的 Executor

    在 API 级别 28 Pie 上 引入了一种新方法Context获取主线程执行器的类getMainExecutor https developer android com reference android content Context
  • 带颜色条的圆形图

    我正在尝试用颜色条绘制圆形图 几乎像这样 然而 颜色条的最小值当前为1 我希望能够将其设置为 0 import pandas as pd import matplotlib pyplot as plt import matplotlib c
  • 如何使用 MATLAB 的 num2str 格式化输出

    我正在尝试在 MATLAB 中将数字数组作为字符串输出 我知道这很容易使用num2str http www mathworks com access helpdesk help techdoc ref num2str html 但我想要逗号
  • 在 Angular dart 中 type() 的另一种方式

    我完成了 Angular dart 教程 但我有一个问题 要声明可用于依赖注入的类型 我必须这样做 class MyAppModule extends Module MyAppModule type RecipeBookController
  • C 中的静态结构初始化

    我有一个struct输入如下图所示 typedef struct position float X float Y float Z float A position typedef struct move position initial
  • Udev 规则不适用于 Ubuntu 12.04 上的 libusb

    我使用的是 Ubuntu 12 04 5 LTS x86 64 我有一个 USB 设备 它使用 libusb 与计算机进行通信 将其插入计算机并运行通信程序后 出现以下错误 libusb couldn t open USB device d
  • 为什么临时对象必须有不同的地址?

    我感兴趣的情况是 const int n1 123 const int n2 123 我知道这就像字面意思一样123是初始化临时的参数int and const只是一个无聊的编译时检查 但我想知道为什么在这种情况下需要不同的临时变量 而不是
  • has_and_belongs_to_many 连接表的 Rails 迁移

    我该如何做script generate migration为 a 创建一个连接表has and belongs to many关系 该应用程序在 Rails 2 3 2 上运行 但我还安装了 Rails 3 0 3 Where class
  • TypeScript + OpenLayers 7:设置和获取功能 ID 失败

    我有一张带有两个自定义按钮的地图 绘制多边形 and 删除功能 它允许我绘制和删除绘制的多边形 此外 我为多边形创建了一个测量叠加 显示了它们的面积 见图 为了识别多边形和覆盖层之间的连接 我尝试在它们上设置相同的 id 以便在删除多边形时
  • 我怎样才能用javascript制作一个简单的wep密钥生成器?

    我正在尝试制作一个 wep 密钥生成器 并且阅读了 wep 密钥的工作原理 但我真的不知道如何开始制作它 任何人都可以给我一个例子或指导我教程吗 我尝试使用谷歌但没有运气 在 JavaScript 中 function generateHe
  • 在 Velocity 模板中按名称引用地图

    很确定这个问题有一个简单的答案 但就是找不到正确的 VTL 语法 在我的上下文中 我传递了一个包含其他地图的地图 我想按名称引用这些内部映射并在我的模板中分配它们 内部地图由应用程序的不同部分构建 然后添加到上下文中 举例来说 public
  • 使用 decltype 的函数参数类型

    注意 这个问题中提供的示例不是生产代码 没有任何意义 它只是为了说明我的问题 我正在测试以下的可能性decltype 特别是如果用它来推导函数参数类型 就会遇到问题 假设有两个类结构如下 struct ClassInt Note no de
  • 如何创建一个在 inputText 和 inputSecret 之间切换的复合组件?

    我正在编写一个 Facelets 复合组件 它根据参数在使用 inputText 和 inputSecret 之间切换
  • “serve”不被识别为运行反应应用程序的内部或外部命令

    serve已使用全局安装npm install g serve命令并且它在本地工作 但部署到Windows服务器给出以下错误 serve 不被识别为内部或外部命令 如何修复这个错误 还有 这个有什么用server js文件在一个react项
  • 从关闭中返回?

    如何从闭包返回而不从包含函数返回 在下面的函数中 return语句实际上返回自GM xmlhttpRequest 不是关闭 当然 我可以看到我可以安排我的代码 以便执行在闭包结束时停止 但我很好奇如何在示例中提前返回 function GM
  • iPhone SDK - 将触摸从 UIViewController 转发到子视图

    我有一个UIViewController其子类为UIView上面叫customSubView1 然后在 customSubView1 上我有另一个子类UIView called customSubView2 当我放置时 我可以捕获所有子视图
  • 违反 MISRA 2012 - 类型不匹配(规则 10.1、10.4)

    我面临着 MISRA C 2012 违规 我无法理解 以下是代码 define I2C CCRH FS uint8 t 0x80 define I2C CCRH DUTY uint8 t 0x40 define I2C CCRH CCR u
  • UIActionSheet 与 swift

    我创建了一个操作表 但问题是没有调用委托方法 myActionSheet UIActionSheet myActionSheet addButtonWithTitle Add event myActionSheet addButtonWit
  • 汇编语言 - masm32 - 乘法

    我将 3 个数字相乘 即使有进位也能很好地工作 我想添加第四个数字来相乘 只是为了学习目的 在我乘以 3 个数字后 我转移到 EDX 并打印 效果很好 在我添加第四个数字后 我认为我正在乘以 32 位 x 32 位 那么它存储到 EDX E
  • 通过接口动态创建类

    我有一些经验 Net Expressions 当我能够动态生成方法时 没关系 很好 但现在我需要生成一个完整的类 似乎唯一的方法就是发出整个IL 这是完全不可接受的 不可能支持 假设我们有以下接口 public interface IFoo