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

发布于 2024-09-07 17:48:25 字数 647 浏览 2 评论 0原文

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

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

感谢您对此的任何帮助, 格热戈日·基茨

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");
    }
}

today I discovered a very strange behavior with C# function overloading. The problem occurs when I have a method with 2 overloads, one accepting Object and the other accepting Enum of any type. When I pass 0 as parameter, the Enum version of the method is called. 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)

Thanks for any help on this,
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");
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

权谋诡计 2024-09-14 17:48:25

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

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
转换为任何枚举类型并且
任何可空类型,其底层
type 是一个枚举类型。在后者中
如果转换是通过以下方式评估的
转换为底层枚举类型
并对结果进行包装(§4.1.10)。

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

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

It may be that you're not aware that there's an implicit conversion from a constant1 of 0 to any enum:

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.

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:

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:

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. In the latter
case the conversion is evaluated by
converting to the underlying enum-type
and wrapping the result (§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.

EDIT: It looks like the "constant" part was partially introduced in the C# 3 compiler. Previously it was some constant values, now it looks like it's all of them.

裂开嘴轻声笑有多痛 2024-09-14 17:48:25

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

好吧,我发现这个,引用以下内容并将其归功于 Eric Gunnerson:

C# 中的枚举有双重用途。它们用于通常的枚举用途,也用于位字段。当我处理位字段时,您经常需要将一个值与位字段进行“与”操作并检查它是否为真。

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

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

我们认为这很难阅读。一种替代方法是定义零条目:

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

这也很丑。

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

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

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

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

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. I will try to find some reference for this...

OK, well I found this, which quotes the following and attributes it to Eric Gunnerson:

Enums in C# do dual purpose. 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.

Our initial rules meant that you had to write:

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)

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)

which is why PlayingCard(0, 0) works.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文