Странное поведение компилятора С# (разрешение перегрузки)

Я обнаружил очень странное поведение компилятора С# для следующего кода:

    var p1 = new SqlParameter("@p", Convert.ToInt32(1));
    var p2 = new SqlParameter("@p", 1);
    Assert.AreEqual(p1.Value, p2.Value); // PASS

    var x = 0;
    p1 = new SqlParameter("@p", Convert.ToInt32(x));
    p2 = new SqlParameter("@p", x);
    Assert.AreEqual(p1.Value, p2.Value); // PASS

    p1 = new SqlParameter("@p", Convert.ToInt32(0));
    p2 = new SqlParameter("@p", 0);
    Assert.AreEqual(p1.Value, p2.Value); // FAIL!?

В последней строке происходит сбой утверждения со следующим сообщением:

  Expected: 0
  But was:  null

Я понимаю, почему тест не проходит: p2 = new SqlParameter("@p", 0); разрешается как SqlParameter(string, SqlDbType), а в остальных случаях как SqlParameter(string, object). Но я не понимаю, почему это происходит. Для меня это выглядит как ошибка, но я не могу поверить, что компилятор C# может иметь такую ​​​​ошибку.

Есть причины для этого?

P.S. Кажется, это проблема для любой перегрузки метода с параметром перечисления и значением 0 (SqlDbType — это перечисление).


person Victor Haydin    schedule 22.11.2011    source источник


Ответы (1)


По сути, десятичный целочисленный литерал 0 неявно преобразуется во все типы перечислений (спецификация C# 4, §6.1.3), поэтому компилятор определяет, что SqlParameter(string, SqlDbType) является применимым членом функции. Затем он должен выбрать лучший из двух кандидатов-членов функции, и он выбирает SqlParameter(string, SqlDbType) вместо SqlParameter(string, object), потому что SqlDbType является более конкретным типом, чем object (§7.5.3.2).

Но я согласен, что в этом случае это очень запутанно...

person Thomas Levesque    schedule 22.11.2011
comment
Просто идея, что произойдет, если установить уровень предупреждения на максимум? Может быть, он предупредит об этом в этом случае. - person dowhilefor; 22.11.2011
comment
@VictorHaydin, потому что так сказано в спецификации ... Неявное преобразование перечисления позволяет преобразовать десятично-целочисленный литерал 0 в любой тип перечисления и в любой тип, допускающий значение NULL, базовый тип которого является типом перечисления. Теперь я не знаю, почему он был разработан таким образом, но, вероятно, есть веская причина... вам нужно спросить кого-нибудь из команды C #. - person Thomas Levesque; 22.11.2011