Странный (возможно неправильно?) поведение компилятора C# с перегрузкой метода и перечислениями

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

Код ниже объясняет проблему (сверился со временем выполнения 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");
    }
}
11
задан Grzegorz Kyc 30 June 2010 в 22:20
поделиться

2 ответа

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

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

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

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

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

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

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

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

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

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

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

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

3
ответ дан 3 December 2019 в 07:36
поделиться

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

Bar x = 0; // Implicit conversion

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

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


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

const decimal DecimalZero = 0.0m;

...
Bar x = DecimalZero;

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

В разделе 6.1.3 спецификации C # (спецификация C # 4) говорится об этом следующее:

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

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

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

13
ответ дан 3 December 2019 в 07:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: