为啥 C# 可以重载具有相同参数的两个方法,只要其中一个具有默认参数?

Posted

技术标签:

【中文标题】为啥 C# 可以重载具有相同参数的两个方法,只要其中一个具有默认参数?【英文标题】:Why could C# overload two methods with the same params as long as one of them has default param?为什么 C# 可以重载具有相同参数的两个方法,只要其中一个具有默认参数? 【发布时间】:2021-11-09 06:53:07 【问题描述】:

我最近注意到 C# 编译器允许方法重载,如下所示:

    public static string foo(string a, bool x = false)
    
        return "a";
    
    public static string foo(string a)
    
        return "b";
    

据我测试,只要没有给出第二个参数,它总是返回“b”,这是有道理的。但是,我认为编译器确实不应该允许这种类型的重载。请问为什么这个功能是这样设计的,而不是编译器报错?

【问题讨论】:

那么这个问题还是陈述?这实际上是 C# ECMA 规范中称为重载解析的一个非常深入的主题,它可以在编译或运行时发生。说某事应该以不同的方式工作是可以的(也许你已经以各种可以想象的方式完全考虑了这一点,所以他们应该聘请你加入 Roslyn 和运行时团队),但是我们无能为力 However, I think the compiler really should not allow this type of overloading. 你为什么这么认为? Why could C# overload two methods with the same params as long as one of them has default param? 显而易见的答案是它们没有具有相同的参数。 你可能会看到这种警告:Method with optional parameter is hidden by overload. 顺便说一句,我发现微软确实在他们的文档中声明了重载解决方案:docs.microsoft.com/en-us/dotnet/csharp/programming-guide/… 【参考方案1】:

虽然这样的问题根本无法回答,但由于无法猜测语言设计者的意图,我可能会猜测一下。

通过转换代码以将参数注入调用站点来处理可选参数。所以

public static void Test(int a = 42)...

...
Test();

将被转换为

public static void Test(int a)...

...
Test(42);

由编译器。从这一点开始,常规重载解决方案可以运行而不会发生冲突。为什么要这样设计?我不知道,但非直观功能的常见原因是向后兼容性或语言兼容性。

因此,在公共 API 中使用可选参数时要非常小心,这一点很重要。由于库的用户将使用编译它的 API 版本中的默认值。不是它运行的版本。

【讨论】:

【参考方案2】:

我无法解释为什么这是设计的一部分,只是简单地解释为什么您会看到在您的测试中偏爱哪种重载。

如果您查看reference documentation,您会注意到以下三个项目符号来描述重载解决方案:

如果方法、索引器或构造函数的每个参数都是可选的,或者按名称或位置对应于调用语句中的单个参数,并且该参数可以转换为类型参数。 如果找到多个候选者,则会将首选转换的重载决策规则应用于明确指定的参数。忽略可选参数的省略参数。 如果两个候选者被判定为同样优秀,则优先选择没有可选参数的候选者,而该候选者的参数在调用中被省略。重载解析通常更喜欢参数较少的候选。

我会断言,在您的测试中,第 3 条最适用于您的观察 - 因为您省略了第二个参数并且任何一种方法都同样好,分辨率有利于您的第二种方法,并且您会看到“b”返回。

【讨论】:

以上是关于为啥 C# 可以重载具有相同参数的两个方法,只要其中一个具有默认参数?的主要内容,如果未能解决你的问题,请参考以下文章

什么是 重载 ?为什么要重载?有何特点?

为啥 C# 在多态中不考虑函数的返回类型?

C# 中关于重载与重写的区别及用法

方法的重载

什么是方法重载?可以定义两个同名但参数类型不同的方法吗?

如何让函数传递具有相同重载的参数?