为任何方法创建 Func 或 Action(在 C# 中使用反射)

2023-12-20

我的应用程序根据设置动态加载 dll 来自数据库(文件、类和方法名称)。为了方便、加速和减少反射的使用,我想要一个缓存......

遵循以下想法:

 MethodInfo.Invoke

没有什么表现性的(反射性能 - 创建委托(C# 属性) https://stackoverflow.com/questions/10820453/reflection-performance-create-delegate-properties-c) 我想翻译任何对方法的调用。我想到了这样的事情:

public static T Create<T>(Type type, string methodName) // or
public static T Create<T>(MethodInfo info) // to use like this:
var action = Create<Action<object>>(typeof(Foo), "AnySetValue");

一项要求是所有参数都可以是对象。

我正在尝试处理表达式,到目前为止我有这样的东西:

    private void Sample()
    {
        var assembly = Assembly.GetAssembly(typeof(Foo));

        Type customType = assembly.GetType("Foo");

        var actionMethodInfo = customType.GetMethod("AnyMethod");
        var funcMethodInfo = customType.GetMethod("AnyGetString");
        var otherActionMethod = customType.GetMethod("AnySetValue");
        var otherFuncMethodInfo = customType.GetMethod("OtherGetString");

        var foo = Activator.CreateInstance(customType);
        var actionAccessor = (Action<object>)BuildSimpleAction(actionMethodInfo);
        actionAccessor(foo);

        var otherAction = (Action<object, object>)BuildOtherAction(otherActionMethod);
        otherAction(foo, string.Empty);

        var otherFuncAccessor = (Func<object, object>)BuildFuncAccessor(funcMethodInfo);
        otherFuncAccessor(foo);

        var funcAccessor = (Func<object,object,object>)BuildOtherFuncAccessor(otherFuncMethodInfo);
        funcAccessor(foo, string.Empty);
    }

    static Action<object> BuildSimpleAction(MethodInfo method)
    {
        var obj = Expression.Parameter(typeof(object), "o");

        Expression<Action<object>> expr =
            Expression.Lambda<Action<object>>(
                Expression.Call(
                    Expression.Convert(obj, method.DeclaringType),
                    method), obj);

        return expr.Compile();
    }

    static Func<object, object> BuildFuncAccessor(MethodInfo method)
    {
        var obj = Expression.Parameter(typeof(object), "o");

        Expression<Func<object, object>> expr =
            Expression.Lambda<Func<object, object>>(
                Expression.Convert(
                    Expression.Call(
                        Expression.Convert(obj, method.DeclaringType),
                        method),
                    typeof(object)),
                obj);

        return expr.Compile();

    }

    static Func<object, object, object> BuildOtherFuncAccessor(MethodInfo method)
    {
        var obj = Expression.Parameter(typeof(object), "o");
        var value = Expression.Parameter(typeof(object));

        Expression<Func<object, object, object>> expr =
            Expression.Lambda<Func<object, object, object>>(
                    Expression.Call(
                        Expression.Convert(obj, method.DeclaringType),
                        method,
                        Expression.Convert(value, method.GetParameters()[0].ParameterType)), 
                        obj, value);

        return expr.Compile();

    }

    static Action<object, object> BuildOtherAction(MethodInfo method)
    {
        var obj = Expression.Parameter(typeof(object), "o");
        var value = Expression.Parameter(typeof(object));

        Expression<Action<object, object>> expr =
            Expression.Lambda<Action<object, object>>(
                Expression.Call(
                    Expression.Convert(obj, method.DeclaringType),
                    method,
                    Expression.Convert(value, method.GetParameters()[0].ParameterType)),
                obj,
                value);

        return expr.Compile();
    }

public class Foo
{
    public void AnyMethod() {}

    public void AnySetValue(string value) {}

    public string AnyGetString()
    {            return string.Empty;        }

    public string OtherGetString(string value)
    {            return string.Empty;        }
}

有什么办法可以简化这段代码吗? (我相信只使用泛型创建一个方法是可能的。)当你有 3、4、5 个参数时,有像我一样的参数吗?


我在想,如果有这样的事情怎么办:

https://codereview.stackexchange.com/questions/1070/generic-advanced-delegate-createdelegate-using-expression-trees https://codereview.stackexchange.com/questions/1070/generic-advanced-delegate-createdelegate-using-expression-trees

但我会有多个参数(在动作或函数中),这个参数(第一个参数)是一个要执行的对象。 这可能吗?


我制作了一个示例程序,可以满足您的所有要求(我认为!)

class Program
{
    class MyType
    {
        public MyType(int i) { this.Value = i; }

        public void SetValue(int i) { this.Value = i; }

        public void SetSumValue(int a, int b) { this.Value = a + b; }

        public int Value { get; set; }
    }

    public static void Main()
    {
        Type type = typeof(MyType);

        var mi = type.GetMethod("SetValue");

        var obj1 = new MyType(1);
        var obj2 = new MyType(2);

        var action = DelegateBuilder.BuildDelegate<Action<object, int>>(mi);

        action(obj1, 3);
        action(obj2, 4);

        Console.WriteLine(obj1.Value);
        Console.WriteLine(obj2.Value);

        // Sample passing a default value for the 2nd param of SetSumValue.
        var mi2 = type.GetMethod("SetSumValue");

        var action2 = DelegateBuilder.BuildDelegate<Action<object, int>>(mi2, 10);

        action2(obj1, 3);
        action2(obj2, 4);

        Console.WriteLine(obj1.Value);
        Console.WriteLine(obj2.Value);

        // Sample without passing a default value for the 2nd param of SetSumValue.
        // It will just use the default int value that is 0.
        var action3 = DelegateBuilder.BuildDelegate<Action<object, int>>(mi2);

        action3(obj1, 3);
        action3(obj2, 4);

        Console.WriteLine(obj1.Value);
        Console.WriteLine(obj2.Value);
    }
}

委托生成器类:

public class DelegateBuilder
{
    public static T BuildDelegate<T>(MethodInfo method, params object[] missingParamValues)
    {
        var queueMissingParams = new Queue<object>(missingParamValues);

        var dgtMi = typeof(T).GetMethod("Invoke");
        var dgtRet = dgtMi.ReturnType;
        var dgtParams = dgtMi.GetParameters();

        var paramsOfDelegate = dgtParams
            .Select(tp => Expression.Parameter(tp.ParameterType, tp.Name))
            .ToArray();

        var methodParams = method.GetParameters();

        if (method.IsStatic)
        {
            var paramsToPass = methodParams
                .Select((p, i) => CreateParam(paramsOfDelegate, i, p, queueMissingParams))
                .ToArray();

            var expr = Expression.Lambda<T>(
                Expression.Call(method, paramsToPass),
                paramsOfDelegate);

            return expr.Compile();
        }
        else
        {
            var paramThis = Expression.Convert(paramsOfDelegate[0], method.DeclaringType);

            var paramsToPass = methodParams
                .Select((p, i) => CreateParam(paramsOfDelegate, i + 1, p, queueMissingParams))
                .ToArray();

            var expr = Expression.Lambda<T>(
                Expression.Call(paramThis, method, paramsToPass),
                paramsOfDelegate);

            return expr.Compile();
        }
    }

    private static Expression CreateParam(ParameterExpression[] paramsOfDelegate, int i, ParameterInfo callParamType, Queue<object> queueMissingParams)
    {
        if (i < paramsOfDelegate.Length)
            return Expression.Convert(paramsOfDelegate[i], callParamType.ParameterType);

        if (queueMissingParams.Count > 0)
            return Expression.Constant(queueMissingParams.Dequeue());

        if (callParamType.ParameterType.IsValueType)
            return Expression.Constant(Activator.CreateInstance(callParamType.ParameterType));

        return Expression.Constant(null);
    }
}

怎么运行的

核心是构建委托 method:

static T BuildDelegate<T>(MethodInfo method)

  • T 是您要创建的委托类型。
  • method 是您希望由生成的委托调用的方法的 MethodInfo。

调用示例:var action = BuildDelegate<Action<object, int>>(mi);

参数规则:

  • 如果传递的方法是实例方法,则生成的委托的第一个参数将接受包含该方法本身的对象实例。所有其他参数都将传递给该方法。

  • 如果传递的方法是静态方法,则生成的委托的所有参数都将传递给该方法。

  • 缺少的参数将传递默认值。

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

为任何方法创建 Func 或 Action(在 C# 中使用反射) 的相关文章

  • 我的线程图像生成应用程序如何将其数据传输到 GUI?

    Mandelbrot 生成器的缓慢多精度实现 线程化 使用 POSIX 线程 Gtk 图形用户界面 我有点失落了 这是我第一次尝试编写线程程序 我实际上并没有尝试转换它的单线程版本 只是尝试实现基本框架 到目前为止它是如何工作的简要描述 M
  • 使用具有现有访问令牌的 Google API .NET 客户端

    用例如下 移动应用程序正在通过 Google 对用户进行身份验证 并且在某些时候 我们需要将用户的视频发布到他的 YouTube 帐户 出于实际原因 实际发布应该由后端完成 已经存储在那里的大文件 由于用户已经通过应用程序的身份验证 因此应
  • 为什么我不能用 `= delete;` 声明纯虚函数?

    Intro 纯虚函数使用通用语法声明 virtual f 0 然而 自 c 11 以来 有一种方法可以显式地传达non existence 特殊 成员函数的 Mystruct delete eg default constructor Q
  • 如何在 Android NDK 中创建新的 NativeWindow 而无需 Android 操作系统源代码?

    我想编译一个 Android OpenGL 控制台应用程序 您可以直接从控制台启动 Android x86 运行 或者从 Android x86 GUI 内的 Android 终端应用程序运行 这个帖子 如何在 Android NDK 中创
  • 为什么要序列化对象需要 Serialized 属性

    根据我的理解 SerializedAttribute 不提供编译时检查 因为它都是在运行时完成的 如果是这样 那么为什么需要将类标记为可序列化呢 难道序列化器不能尝试序列化一个对象然后失败吗 这不就是它现在所做的吗 当某些东西被标记时 它会
  • 使用post方法将多个参数发送到asp.net core 3 mvc操作

    使用 http post 方法向 asp net mvc core 3 操作发送具有多个参数的 ajax 请求时存在问题 参数不绑定 在 dot net 框架 asp net web api 中存在类似的限制 但在 asp net mvc
  • C# 中的接口继承

    我试图解决我在编写应用程序时遇到的相当大的 对我来说 问题 请看这个 为了简单起见 我将尝试缩短代码 我有一个名为的根接口IRepository
  • JSON 数组到 C# 列表

    如何将这个简单的 JSON 字符串反序列化为 C 中的列表 on4ThnU7 n71YZYVKD CVfSpM2W 10kQotV 这样 List
  • C++ 异步线程同时运行

    我是 C 11 中线程的新手 我有两个线程 我想让它们同时启动 我可以想到两种方法 如下 然而 似乎它们都没有按照我的预期工作 他们在启动另一个线程之前启动一个线程 任何提示将不胜感激 另一个问题是我正在研究线程队列 所以我会有两个消费者和
  • ASP MVC:服务应该返回 IQueryable 的吗?

    你怎么认为 你的 DAO 应该返回一个 IQueryable 以便在你的控制器中使用它吗 不 您的控制器根本不应该处理任何复杂的逻辑 保持苗条身材 模型 而不是 DAO 应该将控制器返回给视图所需的所有内容 我认为在控制器类中看到查询 甚至
  • 如何识别 WPF 文本框中的 ValidationError 工具提示位置

    我添加了一个箭头来指示工具提示中的文本框 当文本框远离屏幕边缘时 这非常有效 但是当它靠近屏幕边缘时 工具提示位置发生变化 箭头显示在左侧 Here is the Image Correct as expected since TextBo
  • 将数据打印到文件

    我已经超载了 lt lt 运算符 使其写入文件并写入控制台 我已经为同一个函数创建了 8 个线程 并且我想输出 hello hi 如果我在无限循环中运行这个线程例程 文件中的o p是 hello hi hello hi hello hi e
  • Azure 事件中心 - 按顺序接收事件

    我使用下面的代码从 Azure Event Hub 接收事件 https learn microsoft com en us azure event hubs event hubs dotnet framework getstarted s
  • 基于xsd模式生成xml(使用.NET)

    我想根据我的 xsd 架构 cap xsd 生成 xml 文件 我找到了这篇文章并按照说明进行操作 使用 XSD 文件生成 XML 文件 https stackoverflow com questions 6530424 generatin
  • C# 中条件编译符号的编译时检查(参见示例)?

    在 C C 中你可以这样做 define IN USE 1 define NOT IN USE 1 define USING system 1 system 1 IN USE 进而 define MY SYSTEM IN USE if US
  • 在 C 中使用 GNU automake 中的解析器

    我是 GNU autotools 的新手 在我的项目中使用了 lex 和 yacc 解析器 将它们作为 makefile am 中的源代码会产生以下错误 配置 in AC CHECK PROGS YACC bison yacc none i
  • 如何挤出平面 2D 网格并赋予其深度

    我有一组共面 连接的三角形 即二维网格 现在我需要将其在 z 轴上挤出几个单位 网格由一组顶点定义 渲染器通过与三角形数组匹配来理解这些顶点 网格示例 顶点 0 0 0 10 0 0 10 10 0 0 10 0 所以这里我们有一个二维正方
  • 在类的所有方法之前运行一个方法

    在 C 3 或 4 中可以做到这一点吗 也许有一些反思 class Magic RunBeforeAll public void BaseMethod runs BaseMethod before being executed public
  • 是否可以有一个 out ParameterExpression?

    我想定义一个 Lambda 表达式out范围 有可能做到吗 下面是我尝试过的 C Net 4 0 控制台应用程序的代码片段 正如您在 procedure25 中看到的 我可以使用 lambda 表达式来定义具有输出参数的委托 但是 当我想使
  • 什么是 __declspec 以及何时需要使用它?

    我见过这样的例子 declspec在我正在阅读的代码中 它是什么 我什么时候需要使用这个构造 这是 Microsoft 对 C 语言的特定扩展 它允许您使用存储类信息来赋予类型或函数属性 文档 declspec C https learn

随机推荐

  • 如何从 C++ 向量中获取 2 个随机(不同)元素

    我想从 std vector 中获取 2 个随机不同的元素 我怎样才能做到这一点 它很快 在我的算法中已经完成了数千次 它是优雅的 元素选择确实是均匀分布的 为了优雅和简单 void Choose const int size int fi
  • 如何为复杂的文档结构编写 MongoTemplate 查询和条件?

    我是 MongoTemplate 的新手 我想定义一个 org springframework data mongodb core query Query 和 org springframework data mongodb core qu
  • Makefile 和符号链接

    我在 makefile 方面遇到了一个奇怪的问题 我只想在 makefile 中设置符号链接 但在一台机器上收到错误消息 Linux 2 6 18 238 12 1 el5 make execvp ln Too many levels of
  • 如何在 Cocoa Mac 中以编程方式关闭窗口?

    如何以编程方式关闭 cocoa mac 中的窗口 我使用按钮单击从第一个窗口 xib 打开了第二个窗口 xib 我需要在打开或单击按钮时以编程方式关闭第一个窗口 xib 我怎样才能做到这一点 Apple 有一些有用的示例代码笔尖装载 htt
  • iconv:从 CP1252 转换为 UTF-8

    我正在尝试将 CP1252 编码的字符串 转换为 UTF 8 我尝试过这个命令 iconv c f WINDOWS 1252 t UTF 8 test txt 运气不好 得到了一些奇怪的结果 我尝试在这里输入相同的字符串 他们能够毫无问题地
  • 无法访问sql server配置管理器

    我在删除 SQL Server 2008 R2 Express 后安装了 SQL Server 2008 R2 评估版 卸载后 SQL Server 配置管理器仍然可以访问 但现在无法再访问 并出现以下错误 无法连接到 WMI 提供程序 您
  • 如何设置 VTCompressionSession 的 MaxH264SliceBytes 属性

    iOS VTCompressionSession有一个属性是kVTCompressionPropertyKey MaxH264SliceBytes 但是 我无法设置kVTCompressionPropertyKey MaxH264Slice
  • 如何扩展或覆盖 AngularJS 中现有的过滤器?

    是否可以扩展现有的 标准 过滤器 date number lowercaseETC 就我而言 我需要从 YYYYMMDDhhmmss 格式解析日期 所以我想扩展 或覆盖 date过滤而不是我自己写 我更喜欢实施装饰器模式 http en w
  • Java POS 打印机错误

    我是Java新手 我正在使用java开发POS应用程序 我为这个项目购买了一台 Epson POS 打印机 打印机型号是EPSON TM U220 我已经安装了JavaPos并且 我的代码片段如下 但运行时我收到此错误 感谢是否有人可以帮助
  • 立即音频输入和输出 Android

    在我的 Android 应用程序中 我想从智能手机的麦克风中获取一些音频并立即播放 就像麦克风一样 没有延迟 我目前正在考虑使用AudioRecord and AudioTrack课程 根据我读过的内容 但我不太确定如何继续 我查看了 St
  • devops REST API:无法使用 PATCH 方法更新工作项状态

    我正在使用 DevOps Rest api 来获取一些信息 POST 方法对我来说效果很好 我想更新我的工作项目的状态 为此 我需要使用 PATCH 方法 这不起作用 并且没有给出任何类型的错误 https learn microsoft
  • 在已 root 的 Android 设备上读取或写入 /data 中的文件 [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我正在尝试以编程方式读取 写入已 root 的手机上 data 目录中的文件 我先来外壳执行 su确保我的程序获得超级用户访问权限 效
  • 更改活动选项卡视口大小,如开发工具响应视图

    在 Chrome 扩展中 我想更改视口大小 例如 DevTools 响应式视图 我正在调查https developer chrome com extensions tabs type ZoomSettingsMode https deve
  • 删除mysql表中的所有记录[关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我正在尝试从表中删除所有记录 我的查
  • Django:测试页面是否已重定向到所需的网址

    在我的 Django 应用程序中 我有一个身份验证系统 因此 如果我不登录并尝试访问某些个人资料的个人信息 我会被重定向到登录页面 现在 我需要为此编写一个测试用例 我得到的浏览器响应是 GET myprofile data some id
  • 如何在 MySQL 数据库中存储 UTC ISO8601 日期?

    我有数千个以下格式的日期 2011 10 02T23 25 42Z 又名 UTC 格式的 ISO 8601 我应该使用什么 MySQL 数据类型在 MySQL 数据库中存储这样的 ISO8601 日期 例如 Datetime timesta
  • 如何在 Keycloak 中指定刷新令牌的生命周期

    Keycloak 刷新令牌的生命周期为 1800 秒 refresh expires in 1800 如何指定不同的过期时间 在 Keycloak 管理 UI 中 只能指定访问令牌的生命周期 正如评论中指出的 库巴西蒙诺夫斯基 https
  • 为什么要在mvvm中的viewmodel和view上实现一个接口

    我对 MVVM 模式还很陌生 所以请耐心等待 我见过 wpf mvvm prism 中的实现 其中所有视图都倾向于将 IView 作为最顶层的界面 然后 各个模块中的视图都有一个特定于视图的接口 如 IViewA IViewB 等 它们实现
  • 移动 CALayer(添加动画)

    好吧 我有一个 CALayerlayer我想用 CADisplaylink 移动它 喜欢 layer center CGPointMake layer center x 10 layer center y 10 但我不能用center or
  • 为任何方法创建 Func 或 Action(在 C# 中使用反射)

    我的应用程序根据设置动态加载 dll 来自数据库 文件 类和方法名称 为了方便 加速和减少反射的使用 我想要一个缓存 遵循以下想法 MethodInfo Invoke 没有什么表现性的 反射性能 创建委托 C 属性 https stacko