【问题标题】:Is there a way to treat an IEnumerable<T> as a params T[] object?有没有办法将 IEnumerable<T> 视为 params T[] 对象?
【发布时间】:2015-11-04 16:10:38
【问题描述】:

我想编写将 Sql Server 过程结果作为DataTable 对象返回的通用代码。然后我编写了下面的代码,但是,我偶然发现了DataTable.Rows.Add() 期待params object[] 参数的问题,而我必须传递的是IEnumerable&lt;string&gt;

    public static DataTable GetProcedureResults(int id)
    {
        return GetQueryResultAsDataTable<MyProcedure_Result>(_sqlServerDB.MyProcedure(id));
    }

    private static DataTable GetQueryResultAsDataTable<T>(ObjectResult<T> objectResult)
    {
        DataTable resultAsDataTable = new DataTable("Result");
        var columns = typeof(T).GetProperties();
        foreach (var column in columns)
        {
            resultAsDataTable.Columns.Add(column.Name, typeof(string));
        }

        foreach (var resultRecord in objectResult)
        {
            var fields = resultRecord.GetType().GetProperties().Select(p => p.GetValue(resultRecord).ToString());
            resultAsDataTable.Rows.Add(fields);
        }

        return resultAsDataTable;
    }

熟悉这个问题的朋友可能已经猜到我最后的数据表是这样的:

这些值都被视为一个对象,因此都被插入到第一列中。

这里有什么解决方法(如果有的话)?我在其他帖子中读到似乎 C#6 会修复这个问题,除此之外没有找到清晰简洁的解决方案。

【问题讨论】:

  • 你不能只做resultAsDataTable.Rows.Add(fields.ToArray()); 吗?
  • @DavidL:澄清:我想“展平” IEnumerable 以便它变成一个 params T[] 对象,从而正确填充数据表。例如:如果我的 IEnumerable 是 List {1, 4, 5, 6} 我希望它被视为 datatable.Rows.Add(1, 4, 5, 6);
  • 允许为参数 args 传递 IEnumerable 只是 C#6 的提议;但它没有进入最终版本。

标签: c# parameters system.reflection


【解决方案1】:

如果您有一个 IEnumerable&lt;T&gt; 并且一个方法需要一个 T[],只需在您的枚举上调用 ToArray

var fields = resultRecord.GetType().GetProperties().Select(p => p.GetValue(resultRecord).ToString());
resultAsDataTable.Rows.Add(fields.ToArray());

定义IEnumerable&lt;T&gt;params 参数而不是T[] 的能力并没有最终进入C# 6(请参阅Eric Lippert's answer about the feature,它链接到从C# 6 中删除该功能的公告)。但是,即使具有该理论特征,在这种情况下也无济于事。该功能将允许 API 的设计者(例如编写 DataRowCollection.Add 的人)接受 IEnumerable&lt;T&gt; 而不是 T[],如果他们愿意(在这种情况下,他们几乎肯定不会,因为这将是一个无声的破坏变化)。它不允许您将IEnumerable&lt;T&gt; 传递给期望T[] 的方法。作为方法的调用者,如果您没有使用参数的 params 属性(即传递单个参数而不是集合),那么它只是一个接受数组的“普通”方法,而您在负责将您的参数转换为预期的类型。

【讨论】:

  • 哇,这么简单?我一直在做什么......这就是它。谢谢!
  • 让我明白:它对接收 IEnumerable 或数组的编译器有什么不同?!为什么只有当它是一个数组而不是一个 IEnumerable 时才能展平序列?我的印象是,问题在于编译器无法 flatten 一个 IEnumerable 序列并使其像 e1、e2、e3、e4 一样——这就是 params object[] 对象的样子.为什么它是有限的做一个数组而不是一个 IEnumerable ?
  • 区别; IEnumerable 不是数组;所以当期望一个数组时,一个 IEnumerable 将不适合(另一种方法虽然有效,因为数组是一个 IEnumerable)。至于什么是 IEnumerable,简单地说,它是一个在枚举时会产生(稍后)值的对象,它不直接包含值;它知道如何在请求时提供它们(例如通过 foreach)。不理解扁平化的部分,扁平化更多的是当你有一个容器容器并且你想要一个容器,所有容器值以某种方式连接。
  • 底线是 params 关键字并不神奇:预期的是一个数组对象(由于关键字 params 后面的语法 - [ ]) 的使用,所以唯一需要的只是一个 IEnumerable 到数组的转换,就像我们在其他任何地方都需要做的那样。
  • 基本上是的,params 允许您传递多个“出现”为多个参数的值,但在后台,编译器只是制作数组并将其作为一个整体传递给您(或使用您提供的数组)如果你直接给他一个)
【解决方案2】:

您实际上可以将数组传递给params object[],因此临时解决方案可能只是:

var fields = resultRecord.GetType().GetProperties()
                                   .Select(p => p.GetValue(resultRecord).ToString())
                                   .ToArray();
resultAsDataTable.Rows.Add(fields);

【讨论】:

  • 是的,就是这样,与bdukes提出的解决方案相同。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-06-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-08
  • 2012-02-24
  • 2014-11-09
相关资源
最近更新 更多