为什么C#允许只有方法的最后一个参数是“可变长度”
据我所知,C#允许只有方法的最后一个参数是“可变长度”,例如:
T f(A a, params B[] b)
允许如果你有A r; .... B x, y, z; ....
A r; .... B x, y, z; ....
A r; .... B x, y, z; ....
你可以像f (r, x, y, z)
一样调用f。 为什么C#也没有定义类似的东西:
T f(params A[] a, params B[] b)
因为编译器如何知道第一个参数的变量参数何时停止?
请告诉我argOne
和argTwo
应该包含在方法体内部:
void Foo( params object[] argOne, params object[] argTwo ) { // whatever } Foo( 1, false, "Hello", new object(), 2.3 );
因为确定何时允许这样的结构太复杂了。
(当呼叫明确时)
虽然可以创建一套好的规则,但它们会相当复杂且难以理解。 人们最终会问为什么案例X不起作用,如果它有一个微妙的模糊性。
例如:
- 两种类型都不能是接口或通用参数
- 如果一个类型是枚举或数字类型,则另一个类型必须是除
object
或Enum
之外的类 - 如果一个类型是委托,另一个类型也不能是委托类型(也不是
object
,Delegate
,也不是MulticastDelegate
) - 一种类型不能inheritance另一种
- 所有这些规则都适用于可隐式转换为参数类型的任何类型
- 两种类型都必须
sealed
或必须是值类型
(其中一些规则可以在呼叫站点强制执行)
在实践中,这样的特征会有很多限制,几乎一文不值。
因此,此function将从-10,000点开始 。
它还将创造一个全新的突破性变化类别。 开启类型,添加隐式转换或其他看似微不足道的事情现在可能会破坏客户端代码。
因为含糊不清。 如果你去了并做了:
T f(params int[] a, params int[] b)
你打电话给:
f(1, 2, 3, 4)
编译器如何知道一个开始的位置和另一个开始的位置? 您可以争辩说,这样的情况仍然可以被标记为编译错误并且仍然允许明确的情况继续,但是inheritance它可能变得复杂并且它不值得。 相反,传递两个数组。 我想不出任何值得两个参数数组的情况,即使这样,它也是语法糖。 它与仅使用数组之间没有区别。
(另一个例子:
T f(params string[] a, params object[] b);
string
inheritance自object
。 这仍然含糊不清…)
它会提出问题并导致冲突。
例如,假设B
inheritance自A
编译器将如何理解:
f(A1, A2, )
它是指f({A1, A2}, {B1, B2})
还是f({A1, A2, B1}, {B2})
?
我认为理论上编译器可以是智能的并且可以检测冲突。
然而,最初当.NET和C#本身被设计时,想法是试图避免像这样的明确和棘手的情况。
有许多边缘情况使这成为一个不可能的function。 考虑这个例子:
public static class Coolifier { public static void BeCool(params A[] a, params B[] b) { } } Coolifier.BeCool ("I", "don't", "work.", "DO", "I", "?");
该示例显示了如何无法知道第一个params []的结束位置和下一个params []的开始位置。
void DoSomething( params object[] p1, params int[] p2 ) { ... } DoSomething( 1, 2, 3 );
想想编译器是否可以解决这个问题。 你可以编码’…’部分吗? 如果是的话,它会是可读的吗?
我可以告诉你:这将是一团糟 。
如果“最后一个参数”的长度是可变的,那么你的第二个/第三个/ n个参数就没有必要了,因为根据你的样本,第二个/第三个/ n参数会被包含在第一个参数中并且是不明确的:
T f(params A[] a, params B[] b)
b将包含在a中