繁体   English   中英

奇怪的(可能是错误的?)带有方法重载和枚举的 C# 编译器行为

[英]Strange (possibly wrong?) C# compiler behavior with method overloading and enums

今天我发现了 C# 函数重载的一个非常奇怪的行为。 当我有一个具有 2 个重载的方法时,就会出现问题,一个接受 Object,另一个接受任何类型的 Enum。 当我将 0 作为参数传递时,将调用该方法的 Enum 版本。 当我使用任何其他整数值时,将调用对象版本。 我知道这可以通过使用显式转换轻松解决,但我想知道编译器为什么会这样。 这是一个错误还是我不知道的一些奇怪的语言规则?

下面的代码解释了问题(用运行时 2.0.50727 检查)

感谢您对此的任何帮助,Grzegorz Kyc

class Program
{
    enum Bar
    {
        Value1,
        Value2,
        Value3
    }

    static void Main(string[] args)
    {
        Foo(0);
        Foo(1);
        Console.ReadLine();
    }

    static void Foo(object a)
    {
        Console.WriteLine("object");
    }

    static void Foo(Bar a)
    {
        Console.WriteLine("enum");
    }
}

您可能不知道存在从 0 的常量1到任何枚举的隐式转换:

Bar x = 0; // Implicit conversion

现在,从 0 到Bar的转换比从 0 到object的转换更具体,这就是使用Foo(Bar)重载的原因。

这是否清除了一切?


1 Microsoft C# 编译器中实际上存在一个错误,它允许它是任何零常量,而不仅仅是一个整数:

const decimal DecimalZero = 0.0m;

...
Bar x = DecimalZero;

这不太可能被修复,因为它可能会破坏现有的工作代码。 我相信埃里克利珀有两个博客的帖子,其进入更多的细节。

C# 规范第 6.1.3 节(C# 4 规范)有这样的说法:

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

这实际上表明该错误不仅在于允许错误的类型,还在于允许转换任何常量 0 值,而不仅仅是字面值 0。

编辑:看起来“常量”部分是在 C# 3 编译器中部分引入的 以前它是一些常量值,现在看起来它是全部。

我知道我在其他地方读到过 .NET 系统总是将零视为有效的枚举值,即使它实际上不是。 我会尝试为此找到一些参考...

好的,我找到了这个,它引用了以下内容并将其归因于 Eric Gunnerson:

C# 中的枚举有双重用途。 它们用于通常的枚举用途,也用于位域。 当我处理位域时,您经常希望将一个值与位域进行 AND 运算并检查它是否为真。

我们最初的规则意味着你必须写:

if ((myVar & MyEnumName.ColorRed) != (MyEnumName) 0)

我们认为这很难阅读。 一种选择是定义零条目:

if ((myVar & MyEnumName.ColorRed) != MyEnumName.NoBitsSet)

这也很丑陋。

因此,我们决定稍微放宽我们的规则,并允许从字面量零到任何枚举类型的隐式转换,这允许您编写:

if ((myVar & MyEnumName.ColorRed) != 0)

这就是 PlayingCard(0, 0) 起作用的原因。

因此,这背后的全部原因似乎是在检查标志时简单地允许等于零,而不必强制转换为零。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM