【问题标题】:Params IEnumerable<T> c#参数 IEnumerable<T> c#
【发布时间】:2011-01-08 21:11:19
【问题描述】:

为什么我不能使用带参数的 IEnumerable?这会得到解决吗?我真的希望他们重写旧库以使用泛型......

【问题讨论】:

  • 这是咆哮还是问题?
  • @dtb 显然,两者兼而有之。 +1 的问题。希望我也能给它第二次 +1 的咆哮。
  • @BrainSlugs83 在编辑之前,这个问题看起来像是在咆哮,但是对于问题的标题所传达的内容,这是一个非常好的问题..
  • 回滚到原始问题,主要是因为 Eric Lippert(当时是 C# 编译器开发人员)的以下回答指的是原始措辞。如果他没有对措辞提出异议,我们可能也不需要!
  • 轻微吹毛求疵,params array 仍然是通用的,因此 IEnumerable 不会增加其通用性。可能你的意思是让它更通用(目的)。

标签: c# generics ienumerable params


【解决方案1】:

为什么我不能使用带参数的 IEnumerable?

这个问题的前提是设计团队必须提供一个向语言添加功能的理由。这个假设是错误的。

相反,您需要考虑、设计、指定、实施、测试、记录和交付某个功能。所有这些都有很大的成本。

“params 可枚举”功能已被考虑和设计。它从未被指定、实施、测试、记录或发布。

因此,您无法使用该功能。


更新:在撰写本文时 - 2015 年初 - 现已指定,但在 2014 年下半年 C# 6.0 的实现、测试、文档和发布都被削减了。请参阅 Lucian 的公告:http://roslyn.codeplex.com/discussions/568820

由于尚未实施、测试、记录和发布,因此仍然没有此类功能。希望这将使它成为 C# 的假设未来版本。


更新:我应该澄清我所说的“功能”是什么意思,因为我们的头脑中可能都有不同的想法,什么是“功能”。我说的功能是允许你说类似的东西

void Frob(params IEnumerable<int> x)
{
    foreach(int y in x) ...
}

然后调用站点可以是传递整数序列的“正常形式”,也可以是 Frob(10,20,30) 的“扩展形式”。如果采用扩展形式,编译器会生成调用,就好像您说的是 Frob(new int[] { 10, 20, 30}),这与对 param 数组所做的一样。该功能的要点是,该方法通常从不使用对数组的随机访问,因此,我们可以削弱参数是数组的要求。参数可以只是一个序列。

你今天可以通过重载来做到这一点:

void Frob(params int[] x) { Frob((IEnumerable<int>)x); }

void Frob(IEnumerable<int> x)
{
    foreach(int y in x) ...
}

这有点痛苦。我们可以简单地允许您使用 IEnumerable 作为 params 参数的类型并完成它。

这个问题会得到解决吗?

我希望如此。这个功能已经上榜很久了。它将使许多函数与 LINQ 一起工作得更好。

Frob(from c in customers select c.Age);

无需编写两个不同版本的 Frob。

不过,它只是一个“小便利”的功能;它实际上并没有为语言增加很多新的力量。这就是为什么它从来没有在优先级列表中达到足够高的位置以使其进入“编写规范”阶段。

我真的希望他们重写旧库以使用泛型。

注释已注明。

【讨论】:

  • 实现这个功能基本上意味着对 ToArray 的隐式调用,对吧?
  • 为什么被调用者一定需要那个?我曾经写过的绝大多数采用 params 数组的方法只是把它塞进一个 foreach 中。
  • 我认为这不仅仅是一个便利功能,因为当涉及到泛型时,您的解决方法就会被破坏。很高兴知道它至少在雷达上。 IMO,参数功能应该首先使用 IEnumerable (尽管我当时知道这不是一个选项)。
  • 对于如此看似微不足道的事情,这真是令人痛苦。修复将大大简化 API...请修复 C# 5。拜托?
  • 我今天又一次实现了这个愿望! (这以及缺乏一种简单/干净的方法来创建单个可枚举项开始使 C# 感觉像是一种已经安装了 linq 的“旧”语言......)
【解决方案2】:

啊,我想我现在可能明白你的意思了。我认为您希望能够声明这样的方法:

public void Foo<T>(params IEnumerable<T> items)
{
}

然后可以用这样的“正常”参数调用它:

IEnumerable<string> existingEnumerable = ...;
Foo(existingEnumerable);

或者像这样使用多个参数:

Foo("first", "second", "third");

这就是你所追求的吗? (请注意,您希望第一个表单使用 T=string,而不是使用单个元素的 T=IEnumerable&lt;string&gt;...)

如果是这样,我同意它可能很有用 - 但它很容易拥有:

public void Foo<T>(params T[] items)
{
    Foo((IEnumerable<T>) items);
}

public void Foo<T>(IEnumerable<T> items)
{
}

我发现我这样做的频率不足以使上述问题成为一个特别难看的解决方法。

请注意,在调用上述代码时,您需要显式指定类型参数,以避免编译器偏爱params 示例。比如:

List<string> x = new List<string>();
Foo<string>(x);

【讨论】:

  • 你确定你试过这个吗?当我这样做时,它首先匹配参数表单,即它匹配public void Foo&lt;IEnumerable&lt;int&gt;&gt;(params IEnumerable&lt;int&gt;[])
  • Ok 又测试了一遍,发现哪里错了。您必须准确提供 IEnumerable&lt;T&gt; 而不是 List&lt;T&gt; 类型,否则编译器更喜欢 params 版本。有关可执行示例,请参阅 gist.github.com/bradphelan/9fddca0fa5f1c07ba736
  • @bradgonesurfing:啊,我明白你的意思了。那是因为它是通用的。如果您明确调用Foo&lt;string&gt;,那么它在这两种情况下都有效。我将编辑我的答案以明确这一点。
  • 必须指定泛型类型参数是作为 IEnumerable 的参数是语言的一个很好的补充的一个很好的理由。
  • @SørenBoisen:同意。如果您不指定泛型类型参数,编译器会很高兴地接受它,并且它只会在运行它时失败(希望在单元测试中,而不是在生产中......)。
【解决方案3】:

params 参数作为数组发送,IEnumerable&lt;T&gt; 不提供充当数组所需的随机访问。

调用方法时必须从 IEnumerable 创建数组:

TheMethod(theIEnumerable.ToArray());

【讨论】:

  • 如果方法编写者想要,为什么不能将 params 参数设置为 IEnumerable
  • @Ian Ringrose:一个原因是当params 被添加到语言中时,IEnumerable&lt;T&gt; 接口并不存在。为什么之后没有添加它可能是因为它不是一个有用的功能。您只需添加一个重载即可同时支持params T[]IEnumerable&lt;T&gt;
  • 为什么投反对票?如果你不解释你认为错的地方是什么,它就无法改进答案。
  • @Guffa 不,你不能重载它——当你尝试调用它时编译器会感到困惑,并让你编写一堆额外的语法。 -- 我们还不如添加.ToArray() -- 这很愚蠢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多