class foo
{
public void bar(int i) { ... };
public void bar(long i) { ... };
}
foo.bar(10);
Я ожидал, что этот код выдаст мне какую-то ошибку или, по крайней мере, предупреждение, но не так...
Какая версия bar() вызывается и почему?
class foo
{
public void bar(int i) { ... };
public void bar(long i) { ... };
}
foo.bar(10);
Я ожидал, что этот код выдаст мне какую-то ошибку или, по крайней мере, предупреждение, но не так...
Какая версия bar() вызывается и почему?
Вызывается int-версия bar, потому что 10 является литералом int, и компилятор будет искать метод, который ближе всего соответствует входной переменной (переменным). Чтобы вызвать длинную версию, вам нужно указать длинный литерал, например: foo.bar(10L);
Вот сообщение Эрика Липперта о гораздо более сложных версиях перегрузки методов. Я бы попытался объяснить это, но он работает намного лучше, и я когда-либо мог: http://blogs.msdn.com/b/ericlippert/archive/2006/04/05/odious-ambiguous-overloads-part-one.aspx а>
из спецификации С# 4.0:
Перегрузка методов позволяет нескольким методам в одном классе иметь одно и то же имя, если они имеют уникальные сигнатуры. При компиляции вызова перегруженного метода компилятор использует разрешение перегрузки для определения конкретного вызываемого метода. Разрешение перегрузки находит один метод, который лучше всего соответствует аргументам, или сообщает об ошибке, если единственное наилучшее совпадение не может быть найдено. В следующем примере показано разрешение перегрузки в действии. Комментарий для каждого вызова в методе Main показывает, какой метод вызывается на самом деле.
class Test {
static void F() {
Console.WriteLine("F()");
}
static void F(object x) {
Console.WriteLine("F(object)");
}
static void F(int x) {
Console.WriteLine("F(int)");
}
static void F(double x) {
Console.WriteLine("F(double)");
}
static void F<T>(T x) {
Console.WriteLine("F<T>(T)");
}
static void F(double x, double y) {
Console.WriteLine("F(double,double)");
}
static void Main() {
F(); // Invokes F()
F(1); // Invokes F(int)
F(1.0); // Invokes F(double)
F("abc"); // Invokes F(object)
F((double)1); // Invokes F(double)
F((object)1); // Invokes F(object)
F<int>(1); // Invokes F<T>(T)
F(1, 1); // Invokes F(double, double)
}
}
Как показано в примере, конкретный метод всегда можно выбрать, явно приведя аргументы к точным типам параметров и/или явно указав аргументы типа.
Как говорит Кевин, существует процесс разрешения перегрузок. Основная схема процесса такова:
Фильтры довольно сложные. Например, метод, изначально объявленный в более производном типе, всегда лучше, чем метод, изначально объявленный в менее производном типе. Метод, в котором типы аргументов точно соответствуют типам параметров, лучше, чем метод, в котором есть неточные совпадения. И так далее. Точные правила см. в спецификации.
В вашем конкретном примере алгоритм «лучшести» прост. Точное совпадение int с int лучше, чем неточное совпадение int с long.
Я бы сказал, если вы превысите нижний предел
-2,147,483,648 to 2,147,483,647
управление перейдет к long
Диапазон для long
–9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
Максимальное значение для int
foo.bar(-2147483648);
or
foo.bar(2147483648);
Long получит управление, если мы превысим значение на 2147483648