方法重载解决意外行为

Posted

技术标签:

【中文标题】方法重载解决意外行为【英文标题】:Method overload resolution unexpected behavior 【发布时间】:2011-01-03 20:28:53 【问题描述】:

至少对我来说,我正在与一个奇怪的 .net 方法重载分辨率作斗争。我写了一个小样本来重现这个问题:

class Program

    static void Main(string[] args)
    
        var test = new OverloadTest();
        test.Execute(0);
        test.Execute(1);

        Console.ReadLine();
    


public class OverloadTest

    public void Execute(object value)
    
        Console.WriteLine("object overload: 0", value);
    

    public void Execute(MyEnum value)
    
        Console.WriteLine("enum overload: 0", value);
    


public enum MyEnum
 
    First = 1, Second = 2, Third = 3

将打印:

enum overload: 0
object overload: 1

基本上,调用的重载取决于值 (0, 1) 而不是给定的数据类型。

谁能解释一下?

更新

我应该指出 C# 2 和 C# 3 之间的行为不同

Do((long)0) => object overload //C# 2
Do((long)0) => enum overload   //C# 3 

【问题讨论】:

【参考方案1】:

是的 - 常量 0 隐式 可转换为任何枚举类型。常量 1 只能显式转换为枚举类型。两者都可以隐式转换为object(通过装箱),但在可用的情况下最好转换为枚举。

请注意,这与枚举定义的值无关。任何非零值的转换都是明确的,无论它是否匹配枚举中的值。这只是值 0 的一个特例,这使得其他一些代码更简单(特别是在处理标志时)。恐怕我手头没有规范来查找参考。

额外的奇怪之处:由于 MS 编译器中的一个错误(永远不会被修复 - 它会破坏向后兼容性)它实际上是 various zero constants,而不仅仅是一个整数。所以Execute(0d)Execute(0m) 也会将双精度和十进制转换为枚举。它不适用于 每个 零常数 - 它取决于源代码的确切性质。这一切都非常奇怪 - 请点击 Eric Lippert 揭示所有内容的链接...

【讨论】:

这很奇怪。我本来预计它根本不会编译,因为对 Execute 的两个单独调用是模棱两可的。 0 和 1 是 int 的,因此调用对象重载和 MyEnum 重载同样有效。 我不知道。为什么 0 可以隐式转换为任何枚举类型?在此处的示例中,0 不是有效值。如果某些事情对我的思路没有意义,那并不一定意味着那么多(如 1 分钟前所述);) 它变得比这更奇怪了,因为评估为 0 的常量有时可以隐式转换为枚举,有时不能blogs.msdn.com/ericlippert/archive/2006/03/29/… “在此处的示例中,0 不是有效值”- 不是真的,因为对于 enum,基础类型的 any 值(int in这种情况)也是枚举类型的有效值。只是其中一些值具有符号名称,但大多数没有。选择0 的原因主要是因为它是任何枚举类型的默认值,无论您是否有一个枚举成员映射到 0,并且用于通常使用默认值的地方(例如默认初始化字段等) .当然你也可以写default(Foo),不过那只出现在2.0;而new Foo() 不是很明显。 尼克,将对象重载称为枚举重载并不同样有效。与(1)的调用,别无选择;常量 1 不会隐式转换为枚举。与(0)的调用,有一个选择;要么是好的。在两者都好的情况下,我们选择更具体的类型。由于每个枚举都指向对象,但并非每个对象都指向枚举,因此枚举必须是更具体的类型,因此我们选择它而不是对象。【参考方案2】:

我同意 Jon Skeet 的回答 - 请参阅他在 1 月 11 日 17:32 发布的帖子(上图)。 如需进一步扩展,请参阅 C# 语言规范 - 页:110

6.1.3 隐式枚举转换 隐式枚举转换允许将十进制整数文字 0 转换为任何枚举类型以及任何其基础类型为枚举类型的可空类型。在后一种情况下,通过转换为基础枚举类型并包装结果来评估转换(第 4.1.10 节)。

还是有问题:

添加语句:

test.Execute(-0.0); //对象重载:0

同时添加以下内容:

test.Execute(+0.0); //枚举重载:0

雅克·科尔梅内罗 企业架构师 colmeneroj@videotron.ca

【讨论】:

【参考方案3】:

一个 Enum 只是映射到一个 int (默认情况下)。 0 不会映射到您的 Enum,因此使用了接受对象的重载。 1 映射到您的枚举,因此使用枚举重载。

你可以这样做:

Execute((object) 1);

输出

对象重载:1

【讨论】:

看起来结果实际上与您的预期相反。枚举重载被击中为 0 而不是 1。 我刚刚看了一遍,意识到我误读了这篇文章。我认为 0 正在调用“对象”,而 1 正在调用“枚举”。我必须研究一下,因为 那个 没有多大意义! @statichippo:当您查看规范以及可用的转换时,这非常有意义:) 问题是为什么行为只是像你描述的那样。

以上是关于方法重载解决意外行为的主要内容,如果未能解决你的问题,请参考以下文章

C#中方法重载的不同行为

lua中定义操作符行为的元方法(重载操作符)

重载和重写有啥区别

重载构造方法

构造方法的重载

方法重载和多态