сегодня я обнаружил очень странное поведение с перегрузкой функции 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");
}
}
Я знаю, что где-то еще читал, что система .NET всегда рассматривает ноль как допустимое значение перечисления, даже если на самом деле это не так. Я попытаюсь найти ссылку на это ...
Хорошо, я нашел this , в котором цитируется следующее и приписывается Эрику Ганнерсону:
Перечисления в C # имеют двойное назначение. Они используются для обычного использования перечислений, а также для битовых полей. Когда я имею дело с битовыми полями, вы часто хотите, чтобы значение И с битовым полем было истинным.
Наши первоначальные правила означали, что вы должны были написать:
if ((myVar & MyEnumName.ColorRed)! = (MyEnumName) 0)
, что, по нашему мнению, было трудно читать. Одним из предупреждений было определение нулевой записи:
if ((myVar & MyEnumName.ColorRed)! = MyEnumName.NoBitsSet)
, что также было некрасиво.
Поэтому мы решили немного ослабить наши правила и разрешить неявное преобразование буквального нуля в любой перечисляемый тип, что позволяет записать:
if ((myVar & MyEnumName.ColorRed)! = 0)
, поэтому PlayingCard (0, 0) работает.
Таким образом, похоже, что вся причина этого заключалась в том, чтобы просто позволить приравнять к нулю при проверке флагов без необходимости приводить ноль.
Возможно, вы не знаете, что существует неявное преобразование из константа 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 . Раньше это были некоторые постоянные значения, теперь похоже, что это все они.