Неоднозначный метод перегрузки - PullRequest
5 голосов
/ 26 января 2012

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

class Program
{
    static string Feeling(object o) { return "Lucky"; }
    static string Feeling(string s) { return "Unlucky"; }

    static void Main(string[] args)
    {
        Console.WriteLine("I feel " + Feeling(null));
    }
}

Бонусные баллы начисляются, если вы знаете, какой метод будет называться без кода. И просто чтобы добавить оскорбление, это не просто происходит с нулевыми параметрами:

class Program
{
    static string Feeling(int i) { return "Lucky"; }
    static string Feeling(uint i) { return "Unlucky"; }

    static void Main(string[] args)
    {
        Console.WriteLine("I feel " + Feeling(7));
    }
}

Ответы [ 2 ]

9 голосов
/ 26 января 2012

Первый пример: нулевой аргумент

В первом случае это вызовет перегрузку string. null соответствует обоим object и string, но строка является более конкретным / производным типом. Таким образом он выбирает string.

Проверьте сообщение Эрика Липперта Как система разрешения перегрузки методом решает, какой метод вызывать при передаче нулевого значения? для более подробного объяснения этой части разрешения перегрузки.

Теперь мы должны определить лучшего из подходящих кандидатов. Правила совершенства сложны, но короткая версия состоит в том, что конкретнее лучше, чем менее конкретно.

Второй пример: целочисленный литерал

Во втором случае он выберет первую перегрузку, потому что литерал 7 равен int. Если бы вы использовали 7u, это было бы uint, и поэтому вторая перегрузка была бы предпочтительнее.

Целочисленные литералы имеют четко определенный тип (даже если они допускают больше неявных преобразований, чем обычные интегральные значения). Вы можете использовать суффиксы, такие как u для без знака или l для длительного воздействия на этот тип. Или вы можете добавить явное приведение.

Хотя обычно int не может быть неявно преобразовано в uint, это целое число константа , которое находится в допустимом диапазоне uint, и у компилятора C # есть дополнительное правило чтобы разрешить неявные преобразования между целыми числами констант , при условии, что константа соответствует диапазону цели.

Еще раз Эрик объясняет детали: Почему это неявное преобразование из int в uint работает?

Константное выражение типа int можно преобразовать в тип sbyte, byte, short, ushort, uint или ulong, если значение константы-выражения находится в пределах диапазона целевого типа. Постоянное выражение типа long можно преобразовать в тип ulong при условии, что значение константного выражения не является отрицательным.


В обоих примерах одна перегрузка явно лучшая с точки зрения компилятора C #, и, таким образом, вы не получите неоднозначную ошибку перегрузки.

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

2 голосов
/ 26 января 2012

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

Соответствующим разделом спецификации C # 4 является 7.5.3 в целом (для всего процесса) и 7.5.3.2, чтобы определить, какой применимый элемент функции лучше, когда на первом этапе найдено более одного. (Последующие разделы, такие как 7.5.3.5, содержат подробную информацию о «лучших целях конверсии» и т. Д.)

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

...