【问题标题】:Why C# allows that only the last parameter of a method is of "variable length"为什么 C# 只允许方法的最后一个参数是“可变长度”
【发布时间】:2011-09-15 03:56:26
【问题描述】:

据我所知,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)

【问题讨论】:

  • 如何确定a 的结束位置和b 的开始位置?
  • 在C++中也是这样,我相信C。
  • 我本以为这很明显,正如 zerkms 所说...
  • @zerkms:当完成A类型的元素并启动B类型的元素时
  • @Tom:没有你想的那么明显:stackoverflow.com/questions/6351878/…

标签: c# .net methods parameters


【解决方案1】:

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

请告诉我 argOneargTwo 应该在方法主体内包含什么:

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

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

【讨论】:

  • 如果我是 OP,我会回答编译器不会编译这种不明显的情况,而是编译一些更具确定性的情况,例如 params int[] a, params string[] b ;-)
  • @zerk:是的。但是,规则会非常复杂。看我的回答。
  • @zerkms:您还必须避免从一种类型到另一种类型的隐式转换的情况。过于复杂且不是很有用,无法证明该功能的合理性。
【解决方案2】:

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

例如:

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

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

在实践中,这样的功能会有很多限制,几乎一文不值。

因此,此功能将start with -10,000 points

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

【讨论】:

  • “太复杂”是轻描淡写;在许多情况下这是不可能的(即,当允许隐式转换时)。
  • 即使是狡猾的句法结构之王 C++ 也不允许这样做。
  • @EdS:不;这不是不可能的。它只会有很多Nos。
  • 因此在我的评论中小心使用“许多情况”:)
  • @EdS:我以为你的意思是不可能写出规则。
【解决方案3】:

因为模棱两可。如果你去做了:

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

你打电话给:

f(1, 2, 3, 4)

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

(另一个例子:

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

string 继承自 object。这仍然是模棱两可的......)

【讨论】:

  • 继承使这件事变得复杂的唯一原因是因为 CLR 类型系统的根本故障,它的设计者承认这是一个错误,如果 T1 继承自 T2,则使 Array<T1> 可转换为 Array<T2> .在一致的类型系统中,这根本不是问题。更不用说所有重载决议等都发生在编译时。
  • @DeadMG:就我个人而言,我认为这个系统是有道理的——它允许Array 像任何其他类一样表现,而不是允许特殊行为。即便如此,这并没有真正改变编译器识别两个params 参数数组的方式。
【解决方案4】:

有许多边缘情况使这成为不可能的功能。考虑这个例子:

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", "?");

该示例显示如何无法知道第一个参数 [] 的结束位置和下一个参数的开始位置。

【讨论】:

    【解决方案5】:

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

    例如,假设B 继承自A

    编译器如何理解:

    f(A1, A2, B1, B2)
    

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

    我认为理论上编译器可以很聪明并检测冲突。
    然而,最初设计 .NET 和 C# 本身时的想法是尽量避免这样的明确和棘手的情况。

    【讨论】:

      【解决方案6】:
      void DoSomething( params object[] p1, params int[] p2 )
      {
      ...   
      } 
      
       DoSomething( 1, 2, 3 );
      

      想想编译器是否可以解决这个问题。你能编码'...'部分吗?如果是,它可读吗?

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

      【讨论】:

        【解决方案7】:

        如果“最后一个参数”是可变长度的,则您的第二个/第三个/n 参数不是必需的,因为根据您的样本将包含在第一个参数中并且会模棱两可:

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

        b 将包含在 a 中

        【讨论】:

          猜你喜欢
          • 2023-03-11
          • 1970-01-01
          • 2012-03-11
          • 2014-06-10
          • 2011-02-06
          • 2011-01-10
          • 2010-09-21
          • 2022-11-19
          • 1970-01-01
          相关资源
          最近更新 更多