Какая перегрузка метода С# выбрана?

Почему общий метод вызывается, когда будут совпадать обе перегрузки?

public static void method1(object obj)
{
    Console.WriteLine("Object");
}

public static void method1<T>(T t)
{
    Console.WriteLine("Type T");
}

public static void Main(String args[])
{
    method1("xyz"); //Will print "Type T";
}

Здесь не должно быть конфликтов, не так ли?

Ответ 1

Перегрузки разрешаются путем выбора наиболее конкретной перегрузки. В этом случае method1<string>(string) более специфичен, чем method1(object), так что выбрана перегрузка.

Подробности в разделе раздела 7.4.2 спецификации С#.

Если вы хотите выбрать конкретную перегрузку, вы можете сделать это, явно задав параметры тем типам, которые вы хотите. Следующее вызовет перегрузку method1(object) вместо общего:

method1((object)"xyz"); 

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

void method2(string x, object y);
void method2(object x, string y);

method2("xyz", "abc");

В этом случае компилятор не знает, какую перегрузку выбрать, потому что ни перегрузка явно лучше, чем другая (она не знает, какая строка неявно опущена к объекту). Таким образом, это приведет к ошибке компилятора.

Ответ 2

С# всегда будет выбирать наиболее подходящий метод, который он может выполнить.

При компиляции

method1("xyz");

он будет искать все методы с указанным именем и затем попытаться сопоставить параметры. Компилятор выберет наиболее специфичный метод, в этом случае он предпочел бы

method1(string s)

над

method1<T>(T t) with T = string

и, наконец,

method1(object o)

Обратите внимание на превосходный ответ @Erik на пример, когда компилятор не может решить.

Ответ 3

Поскольку вы уже проходите в T как параметр, поэтому вам не нужно вводить method1<string>("xyz");, вы можете просто пойти method1("xyz");,.Net уже знает строку. Если бы у вас был метод1, тогда это была бы другая история.

Кроме того, поскольку method1(object obj) не принимает строку в качестве параметра, она будет в первую очередь поддерживать общую функцию, где она может заключить T. Если вы должны были изменить method1(object obj) на method1(string obj), то это будет выгодно сначала,

Ответ 4

Как работает перегрузка метода

Чтобы найти подходящую сигнатуру метода для вызова, компилятор выполняет поиск в иерархии типов снизу вверх, а также в виртуальной таблице:

  • Первый в иерархии классов,
  • Затем в иерархии интерфейса.

Потому что классы преобладают на интерфейсах.

Действительно, прежде чем иметь тип интерфейса, объект в первую очередь относится к типу класса.

А неуниверсальные сигнатуры преобладают над общими, поскольку реальность и факты преобладают над абстракцией, если только использование универсального параметра не разрешает вызов более специализированного типа экземпляра.

Применение теории к вопросу

Этот звонок:

method1("xyz");

Совпадение с:

void method1<T>(T t) { }

Перед сопоставлением с:

void method1(object obj)

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

С другой стороны, если вы напишите:

void method1(string obj) { }

void method1<T>(T t) { }

Первый метод называется так.

Пример из практики

var instance = new List<string>();

MyMethod(instance);
MyMethod((IEnumerable<string>) instance);
MyMethod<string>(instance);
MyMethod((object)instance);

void MyMethod<T>(List<T> instance) { }

void MyMethod<T>(IEnumerable<T> list) { }

void MyMethod<T>(T instance) { }

void MyMethod(object instance) { }
  • Первый вызов вызывает первый метод, потому что экземпляр имеет тип List (сопоставление типов).

  • Второй вызов вызывает второй метод из-за побочного приведения (реализации).

  • Третий вызов вызывает третий метод из-за общего параметра, заданного для действия (шаблонизации).

  • Четвертый вызов вызывает четвертый метод из-за приведения вниз (полиморфизм).