Мне кажется, что в компиляторе C # есть ошибка / несоответствие.
Конечно, в компиляторе есть ошибки и несоответствия. Вы не нашли ни одного из них. Компилятор ведет себя совершенно корректно и в соответствии со спецификацией во всех этих случаях.
Я делаю все возможное, чтобы разобраться в этом очень запутанном вопросе. Позвольте мне разбить его на ряд вопросов.
Почему это удается и вызывает первый метод?
public void SomeMethod(string message, object data);
public void SomeMethod(string message, params object[] data);
// ....
SomeMethod("woohoo", item);
(Предположение: этот элемент является выражением типа времени компиляции, отличным от object [].)
Разрешение перегрузки должно выбираться между двумя применимыми методами. Второй метод применим только в расширенном виде. Метод, который применим только в его расширенной форме, автоматически хуже, чем метод, применимый в его обычной форме. Поэтому выбран оставшийся лучший метод.
Почему это не удается из-за ошибки неоднозначности?
public void SomeMethod<T>(string message, T data);
public void SomeMethod<T>(string message, params T[] data);
// ...
SomeMethod("woohoo", (T)item);
Невозможно сказать, потому что вы не говорите, что такое "Т". Откуда T в этом примере? Существует два параметра типа с именем T объявлено; этот код в контексте одного из этих методов? Так как это разных типов, оба с именем T, это может иметь значение. Или это еще третий тип, называемый Т?
Поскольку у вопроса недостаточно информации, чтобы ответить на него, я собираюсь задать лучший вопрос от вашего имени.
Почему это не удается из-за ошибки неоднозначности?
public void SomeMethod<T>(string message, T data);
public void SomeMethod<T>(string message, params T[] data);
// ...
SomeMethod("woohoo", "hello");
Это не так. Это успешно. Вывод типа выбирает "строку" для T в обоих методах. Оба общих метода применимы; вторая применима в развернутом виде, поэтому она проигрывает.
Хорошо, тогда почему это происходит с ошибкой неоднозначности?
public void SomeMethod<T>(string message, T data);
public void SomeMethod<T>(string message, params T[] data);
// ...
SomeMethod("woohoo", null);
Это не так. Он завершается с ошибкой «не могу определить T». Здесь недостаточно информации, чтобы определить, что такое T в любом случае. Поскольку вывод типа не может найти метод-кандидат, набор кандидатов пуст и разрешению перегрузки нечего выбирать.
Так что это удается, потому что ...?
public void SomeMethod<T>(string message, T data);
public void SomeMethod<T>(string message, params T[] data);
// ...
SomeMethod("woohoo", (string)null);
Вывод типа означает, что оба метода являются кандидатами при построении со строкой. Опять же, второй метод хуже, потому что он применим только в развернутом виде.
Что если мы возьмем вывод типа из картинки? Почему это неоднозначно?
public void SomeMethod<T>(string message, T data);
public void SomeMethod<T>(string message, params T[] data);
// ...
SomeMethod<string>("woohoo", null);
Теперь у нас есть три подходящих кандидата. Первый метод, второй метод в его обычной форме и второй метод в его расширенной форме. Расширенная форма отбрасывается, потому что расширенная форма хуже, чем обычно. Это оставляет два метода в их нормальной форме, один принимает строку, а другой - строку []. Что лучше?
Когда мы сталкиваемся с этим выбором, мы всегда выбираем тот, который имеет более конкретный тип. Если бы вы сказали
public void M(string s) { ... }
public void M(object s) { ... }
...
M(null);
мы бы выбрали строковую версию, потому что строка более конкретна, чем объект . Каждая строка является объектом, но не каждый объект является строкой.
строка не преобразуется в строку []. строка [] не может быть преобразована в строку. Ни один из них не является более конкретным, чем другой. Поэтому это ошибка неоднозначности; Есть два «лучших» кандидата.
Тогда почему это удается и что оно делает?
public void SomeMethod<T>(string message, T data);
public void SomeMethod<T>(string message, params T[] data);
// ...
SomeMethod<object>("woohoo", null);
Опять у нас три кандидата, а не два. Мы автоматически исключаем развернутую форму, как и раньше, оставляя две. Из двух методов в нормальной форме, которые остаются, что лучше?
Мы должны определить, что является более конкретным. Каждый объектный массив является объектом, но не каждый объект является объектным массивом. object [] более специфичен, чем object, поэтому мы решили вызвать версию, которая принимает объект []. Мы передаем массив с нулевым параметром, который почти , конечно, не то, что вы хотели.
Вот почему крайне сложно программировать такие перегрузки, как вы. Вы даете своим пользователям возможность столкнуться со всевозможными сумасшедшими двусмысленностями, когда вы делаете подобные вещи. Пожалуйста, не создавайте методы, подобные этому.
Лучший способ создать такую логику: (Обратите внимание, что я на самом деле не скомпилировал этот код, это просто верхзав.)
static string ToString<T>(T t)
{
return t == null ? "" : t.ToString();
}
static string ToString<T>(T t1, T t2)
{
return ToString<T>(t1) + ToString<T>(t2);
}
static string ToString<T>(T t1, T t2, params T[] rest)
{
string firstTwo = ToString<T>(t1, t2);
if (rest == null) return firstTwo;
var sb = new StringBuilder();
sb.Append(firstTwo);
foreach(T t in rest)
sb.Append(ToString<T>(t));
return sb.ToString();
}
Теперь каждый случай обрабатывается с разумной семантикой и приличной эффективностью.И для любого данного сайта вызова вы можете сразу предсказать, какой именно метод будет вызван;Есть только три варианта: один аргумент, два аргумента или более двух аргументов.Каждый обрабатывается однозначно определенным методом.