简体   繁体   English

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

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

today I discovered a very strange behavior with C# function overloading.今天我发现了 C# 函数重载的一个非常奇怪的行为。 The problem occurs when I have a method with 2 overloads, one accepting Object and the other accepting Enum of any type.当我有一个具有 2 个重载的方法时,就会出现问题,一个接受 Object,另一个接受任何类型的 Enum。 When I pass 0 as parameter, the Enum version of the method is called.当我将 0 作为参数传递时,将调用该方法的 Enum 版本。 When I use any other integer value, the Object version is called.当我使用任何其他整数值时,将调用对象版本。 I know this can be easilly fixed by using explicit casting, but I want to know why the compiler behaves that way.我知道这可以通过使用显式转换轻松解决,但我想知道编译器为什么会这样。 Is this a bug or just some strange language rule I don't know about?这是一个错误还是我不知道的一些奇怪的语言规则?

The code below explains the problem (checked with runtime 2.0.50727)下面的代码解释了问题(用运行时 2.0.50727 检查)

Thanks for any help on this, Grzegorz Kyc感谢您对此的任何帮助,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");
    }
}

It may be that you're not aware that there's an implicit conversion from a constant 1 of 0 to any enum:您可能不知道存在从 0 的常量1到任何枚举的隐式转换:

Bar x = 0; // Implicit conversion

Now, the conversion from 0 to Bar is more specific than the conversion from 0 to object , which is why the Foo(Bar) overload is used.现在,从 0 到Bar的转换比从 0 到object的转换更具体,这就是使用Foo(Bar)重载的原因。

Does that clear everything up?这是否清除了一切?


1 There's actually a bug in the Microsoft C# compiler which lets it be any zero constant, not just an integer: 1 Microsoft C# 编译器中实际上存在一个错误,它允许它是任何零常量,而不仅仅是一个整数:

const decimal DecimalZero = 0.0m;

...
Bar x = DecimalZero;

It's unlikely that this will ever be fixed, as it could break existing working code.这不太可能被修复,因为它可能会破坏现有的工作代码。 I believe Eric Lippert has a two blog posts which go into much more detail.我相信埃里克利珀有两个博客的帖子,其进入更多的细节。

The C# specification section 6.1.3 (C# 4 spec) has this to say about it: C# 规范第 6.1.3 节(C# 4 规范)有这样的说法:

An implicit enumeration conversion permits the decimal-integer-literal 0 to be converted to any enum-type and to any nullable-type whose underlying type is an enum-type .隐式枚举转换允许将十进制整数文字0 转换为任何 enum-type 和任何其基础类型为enum-type 的null的类型 In the latter case the conversion is evaluated by converting to the underlying enum-type and wrapping the result (§4.1.10).在后一种情况下,通过转换为基础枚举类型并包装结果来评估转换(第 4.1.10 节)。

That actually suggests that the bug isn't just in allowing the wrong type, but allowing any constant 0 value to be converted rather than only the literal value 0.这实际上表明该错误不仅在于允许错误的类型,还在于允许转换任何常量 0 值,而不仅仅是字面值 0。

EDIT: It looks like the "constant" part was partially introduced in the C# 3 compiler .编辑:看起来“常量”部分是在 C# 3 编译器中部分引入的 Previously it was some constant values, now it looks like it's all of them.以前它是一些常量值,现在看起来它是全部。

I know I have read somewhere else that the .NET system always treats zero as a valid enumeration value, even if it actually isn't.我知道我在其他地方读到过 .NET 系统总是将零视为有效的枚举值,即使它实际上不是。 I will try to find some reference for this...我会尝试为此找到一些参考...

OK, well I found this , which quotes the following and attributes it to Eric Gunnerson:好的,我找到了这个,它引用了以下内容并将其归因于 Eric Gunnerson:

Enums in C# do dual purpose. C# 中的枚举有双重用途。 They are used for the usual enum use, and they're also used for bit fields.它们用于通常的枚举用途,也用于位域。 When I'm dealing with bit fields, you often want to AND a value with the bit field and check if it's true.当我处理位域时,您经常希望将一个值与位域进行 AND 运算并检查它是否为真。

Our initial rules meant that you had to write:我们最初的规则意味着你必须写:

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

which we thought was difficult to read.我们认为这很难阅读。 One alernative was to define a zero entry:一种选择是定义零条目:

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

which was also ugly.这也很丑陋。

We therefore decided to relax our rules a bit, and permit an implicit conversion from the literal zero to any enum type, which allows you to write:因此,我们决定稍微放宽我们的规则,并允许从字面量零到任何枚举类型的隐式转换,这允许您编写:

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

which is why PlayingCard(0, 0) works.这就是 PlayingCard(0, 0) 起作用的原因。

So it appears that the whole reason behind this was to simply allow equating to zero when checking flags without having to cast the zero.因此,这背后的全部原因似乎是在检查标志时简单地允许等于零,而不必强制转换为零。

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

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