【问题标题】:How does the method overload resolution system decide which method to call when a null value is passed?方法重载解析系统如何决定在传递空值时调用哪个方法?
【发布时间】:2023-03-19 04:50:01
【问题描述】:

所以例如你有一个类型:

public class EffectOptions
{
    public EffectOptions ( params object [ ] options ) {}

    public EffectOptions ( IEnumerable<object> options ) {}

    public EffectOptions ( string name ) {}

    public EffectOptions ( object owner ) {}

    public EffectOptions ( int count ) {}

    public EffectOptions ( Point point ) {}

}

这里我只是给出了使用构造函数的例子,但如果它们是类型本身的非构造函数方法,结果将是相同的,对吧?

所以当你这样做时:

EffectOptions options = new EffectOptions (null);

会调用哪个构造函数,为什么?

我可以自己对此进行测试,但我想了解重载解析系统的工作原理(不确定是否是这样的名称)。

【问题讨论】:

  • 如果没有歧义,那么它会调用“最具体”的构造函数。规范中如何定义“最具体”。请参阅stackoverflow.com/questions/3674368/… 以获取相关信息和规范相关(至少一些相关)部分的链接。

标签: c# .net overload-resolution


【解决方案1】:

有关确切规则,请参阅the overload resolution spec。但简而言之,它是这样的。

首先,列出所有可访问的构造函数。

public EffectOptions ( params object [ ] options )
public EffectOptions ( IEnumerable<object> options ) 
public EffectOptions ( string name )
public EffectOptions ( object owner ) 
public EffectOptions ( int count ) 
public EffectOptions ( Point point )

接下来,消除所有不适用的构造函数。适用的构造函数是每个形参都有对应的实参,并且实参可以隐式转换为形参类型的构造函数。假设 Point 是一个值类型,我们消除了“int”和“Point”版本。就这样离开了

public EffectOptions ( params object[] options )
public EffectOptions ( IEnumerable<object> options ) 
public EffectOptions ( string name )
public EffectOptions ( object owner ) 

现在,我们必须考虑带有“params”的那个是否适用于其扩展未扩展形式。在这种情况下,这两种形式都适用。发生这种情况时,我们会丢弃 expanded 表单。这样就离开了

public EffectOptions ( object[] options )
public EffectOptions ( IEnumerable<object> options ) 
public EffectOptions ( string name )
public EffectOptions ( object owner ) 

现在我们必须确定适用候选人的最佳。最佳规则很复杂,但简短的版本是更具体优于不太具体。长颈鹿比哺乳动物更具体,哺乳动物比动物更具体,动物比物体更具体。

object 版本的具体性不如所有版本,因此可以将其删除。 IEnumerable&lt;object&gt; 版本没有object[] 版本那么具体(你明白为什么吗?)所以它也可以被消除。就这样离开了

public EffectOptions ( object[] options )
public EffectOptions ( string name )

现在我们陷入了困境。 object[] 既不比 string 更具体也不比string 更具体。因此,这会产生歧义错误。

这只是一个简短的草图;真正的平局算法要复杂得多。但这些是基础。

【讨论】:

  • 感谢 Eric,感谢您的回复。我不确定为什么 object[] 比 IEnumerable 更具体。是因为 IEnumerable 使用了泛型吗?
  • @Joan:每只长颈鹿都是动物,但不是每只动物都是长颈鹿。因此,长颈鹿比动物更具体。每个 object[] 都是 IEnumerable,但并非每个 IEnumerable 都是 object[]。因此 object[] 比 IEnumerable. 更具体
  • 谢谢 Eric,我从来没有这么想过。
  • @EricLippert,泛型方法如何发挥作用,(在 c# 7.3 之前)从未排除泛型,因为 T 始终是最具体的,因为它引用类型
  • 有关更新的规范链接,请参阅docs.microsoft.com/en-us/dotnet/csharp/language-reference/…
【解决方案2】:

在这种情况下,C# 编译器不会选择任何构造函数,而是会出错。 null 的值对于几个可用的构造函数是合法的,并且没有足够的打破平局逻辑来选择一个,因此会产生错误。

C# 编译器重载解析逻辑是一个复杂的过程,但对其工作原理的简短(且本质上不完整)概述如下

  • 收集具有给定名称的所有成员
  • 将成员筛选为具有与提供的参数兼容且具有适当可访问性的参数列表的成员
  • 如果其余成员有多个元素,则使用平局逻辑来选择其中最好的一个

C# 语言规范的第 7.4 节列出了完整的详细信息。而且我相信埃里克很快就会给出更准确的描述:)

【讨论】:

  • 谢谢贾里德。您所说的“本质上不完整”是什么意思?你的意思是它缺少一些功能?
  • @Joan,我的意思是我的 3 个项目符号摘要不包含重载解决方案固有的许多小细节:运算符、命名 + 可选参数解决方案、覆盖问题与新问题等...... C# 语言规范有完整的细节(大约需要 4-5 页来解释它)。
猜你喜欢
  • 2020-05-08
  • 2010-10-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-29
  • 1970-01-01
  • 2014-07-28
  • 1970-01-01
相关资源
最近更新 更多