Странное (возможно неправильное?) Поведение компилятора C # с перегрузкой метода и перечислениями - PullRequest
11 голосов
/ 01 июля 2010

сегодня я обнаружил очень странное поведение с перегрузкой функции C #. Проблема возникает, когда у меня есть метод с двумя перегрузками, одна принимает объект, а другая принимает Enum любого типа. Когда я передаю 0 в качестве параметра, вызывается версия метода Enum. Когда я использую любое другое целочисленное значение, вызывается версия Object. Я знаю, что это легко исправить, используя явное приведение, но я хочу знать, почему компилятор ведет себя так. Это ошибка или просто какое-то странное правило языка, о котором я не знаю?

Приведенный ниже код объясняет проблему (проверено во время выполнения 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");
    }
}

Ответы [ 2 ]

13 голосов
/ 01 июля 2010

Возможно, вы не знаете, что существует неявное преобразование из константы 1 из 0 в любое перечисление:

Bar x = 0; // Implicit conversion

Теперь преобразование из 0 в Bar более конкретно, чем преобразование из 0 в object, поэтому используется перегрузка Foo(Bar).

Это все проясняет?


1 На самом деле есть ошибка в компиляторе Microsoft C #, которая позволяет ей быть любая нулевая константа, а не просто целое число:

const decimal DecimalZero = 0.0m;

...
Bar x = DecimalZero;

Маловероятно, что это когда-либо будет исправлено,как это может сломать существующий рабочий код.Я полагаю, что у Эрика Липперта есть два блога постов , которые углубляются в детали.

Раздел 6.1.3 спецификации C # (спецификация C # 4) имеет это, чтобы сказатьоб этом:

Неявное преобразование перечисления позволяет преобразовать десятичный-целочисленный-литерал 0 в любой тип перечисления и в любой тип nullable-типа базовый тип которого перечислимый тип .В последнем случае преобразование оценивается путем преобразования в базовый enum-type и переноса результата (§4.1.10).

Это фактически говорит о том, что ошибка не 'Это просто для разрешения неправильного типа, но для преобразования любого значения константы 0, а не только литерального значения 0.

РЕДАКТИРОВАТЬ: похоже, что "константа" была частично введена в C #3 компилятор .Раньше это были некоторые константные значения, теперь похоже, что все они.

3 голосов
/ 01 июля 2010

Я знаю, что где-то еще читал, что система .NET всегда рассматривает ноль в качестве допустимого значения перечисления, даже если на самом деле это не так. Я постараюсь найти ссылку для этого ...

Хорошо, я нашел это , которое цитирует следующее и приписывает это Эрику Ганнерсону:

Перечисления в C # имеют двойное назначение. Они используются для обычного использования перечисления, и они также используются для битовых полей. Когда я имею дело с битовыми полями, вы часто хотите И значение с битовым полем и проверить, если это правда.

Наши первоначальные правила означали, что вы должны были написать:

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

который нам показалось трудным для чтения. Одним из предупреждений было определить нулевую запись:

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

что тоже было некрасиво.

Поэтому мы решили немного ослабить наши правила и разрешить неявное преобразование из буквального нуля в любой тип перечисления, что позволяет написать:

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

, поэтому PlayCard (0, 0) работает.

Таким образом, похоже, что вся причина этого была в том, чтобы просто позволить приравнивать к нулю при проверке флагов, не прибегая к нулю.

...