如何使用表达式来调用以通用列表作为参数的方法调用?

2023-12-01

我们正在使用非常优秀的字符串生成器在我们的项目中作为 ToString 实现的高性能、通用支持。它在调试方面工作得很好,直到我需要生成对象图的字符串表示形式以检查它在加载和关闭之间是否发生了变化。以前我曾使用 MemoryStream 将对象写入 xml,但这看起来很重量级,所以我决定尝试使用 ToStringBuilder,这就是我遇到的麻烦......

我们的对象图大量使用通用类型列表,因此当列表打印出来时,它们看起来如下所示:

PropertyName:{System.Collections.Generic.List`1[Namespace.Path.To.MyClassDto]}

而不是枚举列表并在每个对象上调用 ToString,这很好,因为这是默认行为(顺便说一句,ToStringBuilder 支持 object[],但我们不想仅仅为了解决这个问题而改造整个 Dto 层)。

我尝试修补有问题的代码(ToStringBuilder.cs,第 177 行)来识别类型何时是泛型列表,然后调用 string.Join(", ", list),但我无法理解 linq 反射 API 如何处理泛型。

我尝试的第一件事是获取 String.Join(IEnumerable) 方法的句柄,如下所示:

var stringJoinMethod = typeof(string).GetMethod("Join", new[] { typeof(string), typeof(IEnumerable<>) });

但 GetMethod 返回 null,因此不起作用。我最终发现这个 StackOverflow 问题这向我展示了如何通过签名获取通用方法(改为调用 getmethods() 并过滤结果)。这让我得到了正确的方法句柄,所以我尝试做这样的事情:

private void AppendMember(MemberInfo memberInfo)
{
    AppendQuotesIfRequiredForType(memberInfo);

    Type type = GetMemberType(memberInfo);
    var memberAppendMethod = typeof(StringBuilder).GetMethod("Append", new[] { type });
    Expression getMemberValue = Expression.MakeMemberAccess(TargetArgExpression, memberInfo);

    if (type.IsValueType)
    {
        Type appendArgType = memberAppendMethod.GetParameters()[0].ParameterType;
        if (type != appendArgType)
        {
            getMemberValue = Expression.TypeAs(getMemberValue, typeof(object));
        }
        //my code begins here.
        _appendExpressions.Add(Expression.Call(SbArgExpression, memberAppendMethod, getMemberValue));
    }
    else if (type.IsGenericType && (type.GetGenericTypeDefinition() == typeof(List<>)))
    {
        // now to emit some code to do the below, you wouldn't think it'd be this hard...
        // string.Join(", ", genericList);
        AppendStartOfMembers();

        //this returns null, because generics are not well supported by the reflection API, boo!
        var stringJoinMethod = typeof(string).GetGenericMethod("Join", new[] { typeof(string), typeof(IEnumerable<>) });
        var CommaSpace = Expression.Constant(", ");

        // this doesn't work, throws an ArgumentException as below
        getMemberValue = Expression.Call(stringJoinMethod, CommaSpace, getMemberValue);


        _appendExpressions.Add(Expression.Call(SbArgExpression, memberAppendMethod, getMemberValue));

        AppendEndOfMembers();
    }
    else
    {
        //primitives like strings
        _appendExpressions.Add(Expression.Call(SbArgExpression, memberAppendMethod, getMemberValue));
    }

    //my code ends here.
    AppendQuotesIfRequiredForType(memberInfo);
}

此错误有以下例外:

System.ArgumentException: "Method System.String Join[T](System.String, System.Collections.Generic.IEnumerable`1[T]) is a generic method definition"
   at System.Linq.Expressions.Expression.ValidateMethodInfo(MethodInfo method)
   at System.Linq.Expressions.Expression.ValidateMethodAndGetParameters(Expression instance, MethodInfo method)
   at System.Linq.Expressions.Expression.Call(MethodInfo method, Expression arg0, Expression arg1)
   at MyNameSpace.Common.ToStringBuilder`1.AppendMember(MemberInfo memberInfo) in C:\myproject\MyNamespace.Common\ToStringBuilder.cs:line 206

我开始在谷歌上搜索该错误消息,发现人们在谈论使用 Expression.Lamba() 来包装对泛型方法的调用,此时我意识到我已经超出了我的深度。

因此,假设我有一个 List mylist,如何生成如上所述的表达式,该表达式将执行相当于 string.Join(", ", mylist); 的操作?

thanks!


要获取通用列表的“通用类型”(“T”的类型),您可以这样做

var genericListType= type.GetGenericArguments()[0];

所以在你的 elseif 中,你可以这样做(这可能更容易,我只是尽可能接近你的代码)

else if (type.IsGenericType && (type.GetGenericTypeDefinition() == typeof(List<>)))
    {
        // now to emit some code to do the below, you wouldn't think it'd be this hard...
        // string.Join(", ", genericList);
        AppendStartOfMembers();

        //this returns null, because generics are not well supported by the reflection API, boo!
        var stringJoinMethod = typeof(string).GetGenericMethod("Join", new[] { typeof(string), typeof(IEnumerable<>) });
        var CommaSpace = Expression.Constant(", ");

        var genericListType= type.GetGenericArguments()[0];
        var genericStringJoinMethod = stringJoinMethod.MakeGenericMethod(new[]{genericListType});

        // this doesn't work, throws an ArgumentException as below
        getMemberValue = Expression.Call(genericStringJoinMethod , CommaSpace, getMemberValue);


        _appendExpressions.Add(Expression.Call(SbArgExpression, memberAppendMethod, getMemberValue));

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

如何使用表达式来调用以通用列表作为参数的方法调用? 的相关文章

随机推荐

  • 使用正则表达式进行 Spark 过滤

    我试图将文件数据过滤为每个日期的好数据和坏数据 因此将得到 2 个结果文件 从测试文件中 前 4 行需要输入良好的数据 最后 2 行需要输入错误的数据 我有 2 个问题 我没有得到任何好的数据 结果文件为空 错误的数据结果如下所示 仅选取名
  • 没有 exec 的 fork,以及共享对象使用的 pthread_mutex_t

    我正在处理一个执行以下操作的网络服务器项目fork没有exec 该程序依赖于OpenSSL 而OpenSSL需要一定数量的锁 CRYPTO NUM LOCKS准确地说 目前约为40 锁通常是pthread mutex t在 Linux 上
  • 在 bash 脚本中使用 screen

    我正在远程服务器上运行游戏服务器 我使用独立的屏幕实例使其保持运行 我现在正在创建一个脚本 可用于关闭服务器 备份所有重要文件并再次启动它 但是我在处理屏幕时遇到了一些困难 我假设我可以通过在脚本中调用 screen r 来切换到脚本中的分
  • LINQ 按日期降序对对象集合进行排序

    我有一堆对象 产品 我想先按创建日期降序对它们进行排序 然后只显示前 10 条记录 创建日期 DateTime 的格式如下 4 4 2007 12 00 00 AM 这是我尝试过的 如何按日期降序排列前 10 名 var productLa
  • 将带有复合键/外键的表映射到该表

    我正在尝试映射具有组合键的表并映射引用该表的另一个表 假设这些表 ITEMDELIVERY with relevant columns ITEMDELIVERY ID DELIVERY DATE ITEMDELIVERYDETAIL wit
  • jQuery 中的 jQuery() 函数有什么作用?

    In 这个视频有一段代码是这样的 if jQuery jQuery function 我从未见过jQuery 之前的函数 话又说回来 我不是一个精明的 jQuery 用户 它有什么作用 它是默认随 jQuery 一起发布还是特定于IxEdi
  • 在 Android SDK 管理器中找不到支持包

    我需要支持包 jar 文件 我打开了页面http developer android com sdk compatibility library html Downloading并按照他们所说的去做 启动 SDK 和 AVD 管理器 在 E
  • 无法使用状态值作为子组件的 props

    在我的 React js 应用程序中 我似乎无法使用状态值作为子组件的道具 在父组件中 constructor 应用程序有一个称为空状态selectedWarehouseID 这种状态 selectedWarehouseID 应该更新一些信
  • 如何打开特定相册或文件夹的默认图库应用程序?

    我在网上找到的每个例子都是打开图库并从图库中获取图像作为结果 我的需要是我不想将结果或图像添加到我的应用程序中 我只想触发图库应用程序来显示特定的图像文件夹 我的应用程序有单独的文件夹来保存图像 我需要将用户直接导航到该路径 试试这个代码
  • 在 Jscript 中获取给定用户的特殊文件夹路径

    如何获取当前用户以外的特定用户的 shell 文件夹 例如 本地设置 或 本地应用程序数据 的路径 虽然有一些方法可以在 Windows Script Host 中获取特殊文件夹路径 WshShell SpecialFolders and
  • odeint 简单一维 ode 示例无法编译

    我试着跑指定示例在 Debian Squeeze g 4 4 上的 boost 1 54 0 中 洛伦兹系统工作正常 但是简单的一维颂歌 include
  • VB.Net默认表单实例

    我已替换 正则表达式 所有对默认表单实例在我的项目中 我现在想确保我不再使用默认实例 似乎默认实例功能无法禁用 不改变类的ctor 任何人都知道如何在 VB Net 中查找默认表单实例的所有用法 或者 使用类名访问非共享成员 仅供参考 我正
  • 使用 python Flask 从数据库中删除行?

    我正在使用 Flask 框架 似乎无法从数据库中删除行 下面的代码给出了 405 错误 所请求的 URL 不允许使用该方法 有任何想法吗 在py中 app route delete
  • 如何在 jquery 中包含 !important

    我正在尝试使用 jQuery 在 css 属性中添加 important tabs css height 650px important 但是 important 没有效果 如何在 jquery 中包含 important 显然可以在 jQ
  • 运算符重载:无法添加两个指针

    我创建了一个 Fraction 类 它具有用于在两个 Fraction 对象之间进行加 减 乘和除的成员函数以及所需的默认构造函数和复制构造函数 对于这个问题 我必须使用指针 不能使用向量 因为只有用户选择才能创建 Fraction 对象
  • GatsbyJS 与 Firebase - WebpackError:ReferenceError:IDBIndex 未定义

    我收到错误盖茨比发展 它与这个非常相似 https github com firebase firebase js sdk issues 2222 但我收到错误盖茨比发展 而不是盖茨比构建 我做了很多研究 但找不到有效的解决方案 起初我有一
  • MySQL 内连接查询语法错误

    我是一个 MySQL 新手 无法弄清楚这里出了什么问题 我有两张桌子 左表称为 锻炼 相关列是date 输入日期 和id 类型 int 右表称为Workout locations 相关领域 workout id type int and l
  • 通过撰写导航传递 Parcelable 参数

    我想传递一个可分割的对象 BluetoothDevice 使用组合导航转换为可组合项 传递原始类型很容易 composable profile userId arguments listOf navArgument userId type
  • 以同样的方式随机化两个数组 Swift

    我知道有一种新的随机播放方法iOS 9但我想知道是否有办法以相同的方式对两个数组进行洗牌 例如 1 2 3 4 and a b c d shuffle 3 4 1 2 and c d a b 使用shuffle 方法来自如何在 Swift
  • 如何使用表达式来调用以通用列表作为参数的方法调用?

    我们正在使用非常优秀的字符串生成器在我们的项目中作为 ToString 实现的高性能 通用支持 它在调试方面工作得很好 直到我需要生成对象图的字符串表示形式以检查它在加载和关闭之间是否发生了变化 以前我曾使用 MemoryStream 将对