为什么C#允许通过可选参数进行模糊函数调用?

时间:2016-06-18 07:22:30

标签: c# overloading ambiguity optional-arguments

我今天遇到了这个,我很惊讶我之前没有注意到它。给出一个简单的C#程序,类似于以下内容:

public class Program
{
    public static void Main(string[] args)
    {
        Method(); // Called the method with no arguments.
        Method("a string"); // Called the method with a string.

        Console.ReadLine();
    }

    public static void Method()
    {
        Console.WriteLine("Called the method with no arguments.");
    }

    public static void Method(string aString = "a string")
    {
        Console.WriteLine("Called the method with a string.");
    }
}

您可以获得每个方法调用的注释中显示的输出。

我理解为什么编译器会选择它所执行的重载,但为什么首先允许这样做呢?我不是在问什么是重载决策规则,我理解这些规则,但是我想知道编译器是否允许具有相同签名的基本上两个重载的技术原因?

据我所知,一个带有签名的函数重载只是通过另外一个可选参数与另一个重载不同,只提供了只需要参数(和所有前面的参数)所需的。

它确实做的一件事是让一个程序员(可能没有足够的注意力)认为他们正在调用与他们实际上不同的重载。

我认为这是一个相当不常见的案例,为什么允许这样做的答案可能只是因为它不值得让它不复杂,但C#是否允许函数重载是另一个原因通过另外一个可选参数来区别于其他人吗?

3 个答案:

答案 0 :(得分:14)

  

他的观点是Eric Lippert可以得到一个答案引导我进入https://meta.stackoverflow.com/a/323382/1880663,这听起来像我的问题只会让他烦恼。我会试着改写一下,让我更清楚我在询问语言设计,而且我不是在寻找规范参考

我很感激!我很高兴谈论语言设计;令我恼火的是,当提问者非常不清楚实际上会满足他们的要求时,我会浪费时间。我认为你的问题清楚明了。

Hans发布的问题评论是正确的。语言设计团队非常了解您提出的问题,这远远不是可选/命名参数创建的唯一潜在歧义。我们长期考虑了许多场景,并尽可能仔细地设计了该功能,以减轻潜在的问题。

所有设计流程都是竞争设计原则之间妥协的结果。显然,有许多论点需要与重要的设计,实现和测试成本以及混淆,错误等形式的用户成本之间的平衡,以及意外构造模糊性等问题。你指出一个。

我不打算重复几十个小时的辩论;让我给你高分。

正如Hans所说,该功能的主要推动方案是受欢迎的需求,特别是来自使用C#with Office的开发人员。 (并且充分披露,作为在我加入C#团队之前为Word和Excel编写C#编程模型的团队中的一员,我确实是第一个要求它的人;具有讽刺意味的是我必须实现< / em>几年后这个困难的功能并没有丢失在我身上。)Office对象模型被设计为从Visual Basic中使用,Visual Basic是一种长期具有可选/命名参数支持的语言。

在明显的功能方面,C#4可能看起来像是一个“瘦”的版本。这是因为该版本中的许多工作都是基础设施,可以与为动态语言设计的对象模型实现更加无缝的互操作性。动态类型功能是显而易见的,但是还添加了许多其他小功能,它们结合在一起使得动态和遗留COM对象模型的使用变得更加容易。命名/可选参数只是其中之一。

事实上,我们现有的语言就像VB一样具有这种特定功能已有数十年而且世界尚未结束,这进一步证明了该功能既可行又有价值。在设计新版本的功能之前,您可以从中获得成功和失败的例子,这很棒。

至于你提到的具体情况:我们考虑做一些事情,比如检测何时可能存在歧义并发出警告,但这会打开另外一堆蠕虫。警告必须是常见的,合理的,几乎肯定是错误的代码,应该有一个明确的方法来解决导致警告消失的问题。编写模糊检测器是很多工作;相信我,在重载分辨率中编写模糊度检测比编写代码来处理成功案例要花费更长的时间。我们不想花费大量时间为难以察觉的罕见情况添加警告,并且可能没有明确建议如何消除警告。

另外,坦率地说,如果您编写的代码中有两个名称相同的方法,根据您调用的方法执行完全不同的操作,您的手上已经存在更大的设计问题!首先解决这个问题,而不是担心有人会不小心打错方法;使它成为正确的方法之一。

答案 1 :(得分:11)

此行为由Microsoft在MSDN上指定。看看Named and Optional Arguments (C# Programming Guide)

  

如果两个候选人被判断为同样好,则优先选择没有可选参数的候选人,其中参数在呼叫中被省略。这是对具有较少参数的候选者的重载分辨率的一般偏好的结果。

他们决定以这样的方式实现它的原因可能是,如果你想在之后重载一个方法。因此,您不必更改已编写的所有方法调用。

<强>更新

我很惊讶,Jon Skeet也no real explantation他们为什么这样做。

答案 2 :(得分:3)

我认为这个问题基本上归结为这些签名是如何用中间语言表示的。请注意,两个重载的签名都不等于!第二种方法有这样的签名:

.method public hidebysig static void Method([opt] string aString) cil managed
{
    .param [1] = string('a string')
    // ...
}

在IL中,方法的签名是不同的。它需要一个字符串,标记为可选。这会更改参数初始化的行为,但不会更改此参数的存在。

编译器无法决定您调用哪种方法,因此它根据您提供的参数使用最适合的方法。由于您没有为第一次调用提供任何参数,因此它假定您在没有任何参数的情况下调用重载。

最后,这是一个关于良好代码设计的问题。根据经验,我要么使用可选参数或重载,这取决于我想要做什么:如果方法中的逻辑不依赖于提供的参数,则可选参数是好的,而重载可以提供不同的实现对于不同的论点集。如果你发现自己检查参数是否等于默认值以决定做什么,你可能应该去过载。另一方面,如果您发现自己在许多重载中重复大量代码,则应尝试提取可选参数。

Chuck Skeet to this question也是一个很好的答案。