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

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

    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!?

В последней строке assert не получается следующее сообщение:

  Expected: 0
  But was:  null

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

Любые причины для этого?

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

Ответ 1

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

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