方法重载决策系统在传递空值时如何确定调用哪个方法?

38

比如说你有这样一个类型:

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);
哪个构造函数会被调用,为什么会被调用?
我可以自己测试,但我想了解重载决议系统是如何工作的(不确定它是否被称为这种方式)。

如果不含糊,那么它将调用“最具体”的构造函数。 “最具体”是如何定义的在规范中有说明。请参见https://dev59.com/wnA65IYBdhLWcg3wsw8X以获取相关信息和链接到规范的至少*一些相关部分。 - Jim Mischel
2个回答

86

有关确切的规则,请参见重载决策规范。但简单来说,是这样的。

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

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" 的形式是适用于其 展开的 还是 未展开的 形式。在这种情况下,它适用于两种形式。当发生这种情况时,我们放弃展开的形式。那就只剩下:

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

现在我们必须确定适用候选者中的最佳。最佳规则很复杂,但简短版是越具体越好。长颈鹿比哺乳动物更具体,哺乳动物比动物更具体,动物比对象更具体。

对象版本不如它们具体,所以可以被排除。 IEnumerable<object>版本不如object[]版本具体(你看出原因了吗?),因此也可以被排除。这就留下了:

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

现在我们陷入了困境。object[]string一样具体,因此会导致模糊错误。

这只是简要概述;实际的决定性算法要复杂得多。但那些都是基础知识。


谢谢Eric,感谢您的回复。我不太确定为什么object[]比IEnumerable<object>更具体。是因为IEnumerable使用了泛型吗? - Joan Venge
19
@Joan: 每只长颈鹿都是一种动物,但不是每种动物都是长颈鹿。因此,长颈鹿比动物更加具体。每个object[]都是一个IEnumerable<object>,但不是每个IEnumerable<object>都是一个object[]。因此,object[]比IEnumerable<object>更加具体。 - Eric Lippert
谢谢Eric,我从没这样想过。 - Joan Venge
感谢澄清。他试图声称其泛型不够具体。我的观点是,它们更具体,因为它们专门针对该类型,并且它们不会被排除在外,因为 where 参数被考虑在内,因为它们不是正式参数的一部分。 - johnny 5
1
有关最新规范链接,请参见 https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/expressions#overload-resolution。 - ezolotko
显示剩余6条评论

9
在这种情况下,C#编译器不会选择任何构造函数,而是会出错。值null对于可用的多个构造函数是合法的,但由于缺乏决策逻辑来选择其中一个,因此会产生错误。
C#编译器重载解析逻辑是一个复杂的过程,但它的简要(并固有的不完整)概述如下:
- 收集具有给定名称的所有成员。 - 过滤成员,以使其参数列表与提供的参数兼容,并具有适当的可访问性。 - 如果剩余成员有多个元素,则采用决策逻辑来选择最佳成员。
完整的细节列在C#语言规范的第7.4节中。我相信Eric很快就会来给出更准确的描述 :)

谢谢Jared。你所说的“本质上不完整”是什么意思?你是指它缺少一些功能吗? - Joan Venge
@Joan,我的三个要点摘要并不包含与重载解析相关的许多细节:运算符、命名+可选参数解析、覆盖与新问题等等... C#语言规范有完整的细节(需要大约4-5页来解释所有内容)。 - JaredPar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接