为什么C#允许方法中只有最后一个参数是“可变长度”

问题描述 投票:0回答:7

据我所知,C# 只允许方法的最后一个参数是“可变长度”,例如:

T f(A a, params B[] b)
允许如果您有
A r; .... B x, y, z; ....
,您可以像
f (r, x, y, z)
一样调用 f。为什么 C# 没有定义类似的东西:

T f(params A[] a, params B[] b)
c# .net methods parameters
7个回答
12
投票

因为编译器如何知道第一个参数的变量参数何时停止?

请告诉我方法体内应该包含什么

argOne
argTwo
:

void Foo( params object[] argOne, params object[] argTwo )
{
    // whatever
} 

Foo( 1, false, "Hello", new object(), 2.3 );

9
投票

因为确定何时真正允许这样的构造太复杂了。
(当调用明确时)
虽然可以创建一套好的规则,但它们会相当复杂且难以理解。人们最终会问为什么 case X 不起作用,如果它有微妙的歧义。

例如:

  • 这两种类型都不能是接口或泛型参数
  • 如果一种类型是枚举或数字类型,则另一种类型必须是除
    object
    Enum
  • 之外的类
  • 如果一种类型是委托,则另一种类型不得也是委托类型(也不是
    object
    Delegate
    也不是
    MulticastDelegate
  • 一种类型不能继承另一种类型
  • 所有这些规则适用于任何可隐式转换为参数类型的类型
  • 两种类型都必须是
    sealed
    或者必须是值类型

(其中一些规则可以在调用站点强制执行)

在实践中,这样的功能会有很多限制,几乎毫无价值。

因此,此功能将从 -10,000 点开始

它还将创建一个全新的突破性变革类别。解封类型、添加隐式转换或其他看似微不足道的事情现在可能会破坏客户端代码。


1
投票
因为含糊不清。如果你去了并且做了:

T f(params int[] a, params int[] b)

你打电话来:

f(1, 2, 3, 4)

编译器如何知道一个从哪里开始,另一个从哪里开始?您可能会争辩说,此类情况仍然可以被标记为编译错误,并且仍然允许明确的情况继续,但是使用继承,它可能会变得复杂,而且不值得。而是传递两个数组。我想不出任何需要两个参数数组的情况,即使如此,它也是语法糖。它和仅使用数组没有区别。

(另一个例子:

T f(params string[] a, params object[] b);

string

继承自
object
。这还是模棱两可的……)


1
投票
有很多边缘情况使得这个功能不太可能实现。考虑这个例子:

public static class Coolifier { public static void BeCool<A,B>(params A[] a, params B[] b) { } } Coolifier.BeCool<string,string> ("I", "don't", "work.", "DO", "I", "?");

该示例展示了如何无法知道第一个 params [] 在哪里结束以及下一个参数从哪里开始。


1
投票
这会提出问题并导致冲突。

例如,假设

B

 继承自 
A

编译器如何理解:

f(A1, A2, B1, B2)

这意味着

f({A1, A2}, {B1, B2})

还是
f({A1, A2, B1}, {B2})

我认为理论上编译器可以很智能并检测冲突。

然而,最初设计 .NET 和 C# 本身时,我们的想法是尽量避免像这样明确且棘手的情况。


0
投票
void DoSomething( params object[] p1, params int[] p2 ) { ... } DoSomething( 1, 2, 3 );

想一想编译器是否可以解决这个问题。你能编写“...”部分的代码吗?如果是的话,可以阅读吗?

我可以告诉你:这将是一个

混乱


0
投票
如果“最后一个参数”的长度是可变的,那么你的第二个/第三个/n个参数就不是必需的,因为根据你的示例,它会包含在第一个参数中并且会不明确:

T f(params A[] a, params B[] b)

b 将包含在 a

© www.soinside.com 2019 - 2024. All rights reserved.