У меня есть этот класс
public class Overloaded
{
public void ComplexOverloadResolution(params string[] something)
{
Console.WriteLine("Normal Winner");
}
public void ComplexOverloadResolution<M>(M something)
{
Console.WriteLine("Confused");
}
}
Если я назову это так:
var blah = new Overloaded();
blah.ComplexOverloadResolution("Which wins?");
Пишет Normal Winner
в консоль.
Но, если я добавлю еще один метод:
public void ComplexOverloadResolution(string something, object somethingElse = null)
{
Console.WriteLine("Added Later");
}
Я получаю следующую ошибку:
Вызов неоднозначен для следующих методов или свойств:> '
Overloaded.ComplexOverloadResolution(params string[])
' и 'Overloaded.ComplexOverloadResolution<string>(string)
'
Я понимаю, что добавление метода может привести к неоднозначности вызова, но это неоднозначность между двумя уже существующими методами (params string[])
и <string>(string)
! Очевидно, что ни один из двух методов, участвующих в неоднозначности, не является вновь добавленным методом, потому что первый - это параметры, а второй - общий.
Это ошибка? В какой части спецификации сказано, что так должно быть?
c#
overload-resolution
Маккей
источник
источник
'Overloaded.ComplexOverloadResolution(string)'
относится к<string>(string)
методу; Я думаю, это относится к(string, object)
методу без объекта.Ответы:
Да.
Поздравляем, вы обнаружили ошибку в разрешении перегрузки. Ошибка воспроизводится в C # 4 и 5; не воспроизводится в версии семантического анализатора "Рослин". Я проинформировал команду тестирования C # 5, и, надеюсь, мы сможем исследовать эту проблему и решить ее до финального выпуска. (Как всегда, никаких обещаний.)
Далее следует правильный анализ. Кандидатами являются:
Нулевой кандидат явно неприменим, потому что
string
не может быть преобразован вstring[]
. Остается три.Из трех мы должны определить единственный лучший метод. Мы делаем это путем попарного сравнения трех оставшихся кандидатов. Таких пар три. Все они имеют идентичные списки параметров после того, как мы удалим пропущенные необязательные параметры, что означает, что мы должны перейти к расширенному раунду разрешения конфликтов, описанному в разделе 7.5.3.2 спецификации.
Что лучше: 1 или 2? Уместное решение проблемы заключается в том, что универсальный метод всегда хуже, чем неуниверсальный. 2 хуже 1. Значит, 2 не может быть победителем.
Что лучше: 1 или 3? Соответствующее решение для разрешения конфликтов: метод, применимый только в его развернутой форме, всегда хуже, чем метод, применимый в его нормальной форме. Следовательно, 1 хуже 3. Значит, 1 не может быть победителем.
Что лучше, 2 или 3? Уместное решение проблемы заключается в том, что универсальный метод всегда хуже, чем неуниверсальный. 2 хуже 3. Итак, 2 не может быть победителем.
Чтобы быть выбранным из множества подходящих кандидатов, кандидат должен быть (1) непобежденным, (2) превзойти хотя бы одного другого кандидата и (3) быть уникальным кандидатом, который имеет первые два свойства. Кандидат номер три не побежден ни одним другим кандидатом, и побеждает по крайней мере еще одного кандидата; это единственный кандидат с этим свойством. Следовательно, третий кандидат - единственный лучший кандидат . Он должен победить.
Компилятор C # 4 не только ошибается, но, как вы правильно заметили, выдает странное сообщение об ошибке. Немного удивительно, что компилятор ошибается при анализе разрешения перегрузки. Совершенно неудивительно, что сообщение об ошибке выводится неправильно; эвристика ошибок «неоднозначного метода» в основном выбирает любые два метода из набора кандидатов, если лучший метод не может быть определен. Не очень хорошо находить «настоящую» двусмысленность, если она действительно существует.
Можно резонно спросить, почему это так. Довольно сложно найти два метода, которые были бы «однозначно неоднозначными», потому что отношение «лучшего» непереходно . Возможны ситуации, когда кандидат 1 лучше 2, 2 лучше 3 и 3 лучше 1. В таких ситуациях мы не можем добиться большего, чем выбрать два из них как «неоднозначные».
Я хотел бы улучшить эту эвристику для Roslyn, но это низкий приоритет.
(Упражнение для читателя: «Разработайте алгоритм линейного времени для определения уникального лучшего члена набора из n элементов, в котором отношение лучшего является непереходным», - вот один из вопросов, который мне задали в день собеседования с этой командой. Это не очень сложный алгоритм; попробуйте.)
Одной из причин, по которой мы так долго откладывали добавление необязательных аргументов в C #, было количество сложных неоднозначных ситуаций, которые он вводит в алгоритм разрешения перегрузки; очевидно, мы не поняли это правильно.
Если вы хотите указать проблему с подключением, чтобы отслеживать ее, не стесняйтесь. Если вы просто хотите, чтобы это было доведено до нашего сведения, считайте, что это сделано. Я проведу тестирование в следующем году.
Спасибо, что обратили на это мое внимание. Приносим извинения за ошибку.
источник
Раздел 7.5.3 (разрешение перегрузки), а также разделы 7.4 (поиск членов) и 7.5.2 (определение типа).
Особо обратите внимание на раздел 7.5.3.2 (улучшенный функциональный член), в котором частично говорится, что «необязательные параметры без соответствующих аргументов удаляются из списка параметров» и «Если M (p) - неуниверсальный метод, то M (q) - общий метод, то M (p) лучше, чем M (q) ».Однако я недостаточно хорошо разбираюсь в этих частях спецификации, чтобы знать, какие части спецификации управляют этим поведением, не говоря уже о том, чтобы судить о соответствии.
источник
вы можете избежать этой двусмысленности, изменив имя первого параметра в некоторых методах и указав параметр, который вы хотите назначить
как это :
источник
Если вы удалите
params
из своего первого метода, этого не произойдет. У первого и третьего метода есть оба действительных вызоваComplexOverloadResolution(string)
, но если у вас первый методpublic void ComplexOverloadResolution(string[] something)
, двусмысленности не будет.Предоставление значения для параметра
object somethingElse = null
делает его необязательным параметром, и поэтому его не нужно указывать при вызове этой перегрузки.Изменить: компилятор делает здесь сумасшедшие вещи. Если вы переместите третий метод в код после первого, он сообщит правильно. Кажется, он берет первые две перегрузки и сообщает о них, не проверяя правильность.
Edit2: Новая находка. Удаление любого метода из трех приведенных выше не приведет к неоднозначности между ними. Таким образом, кажется, что конфликт возникает только при наличии трех методов, независимо от порядка.
источник
Если вы напишете
или просто напишите
он попадет в тот же метод , в метод
Это
params
ключевое слово cause, которое делает его наиболее подходящим также для случая, когда не указан параметрЕсли вы попытаетесь добавить новый метод вроде этого
Он отлично скомпилирует и вызовет этот метод, поскольку он идеально подходит для вашего вызова с
string
параметром. Тогда намного сильнееparams string[] something
.Вы объявляете второй метод, как и
Компилятор совершенно запутался между первым методом и этим, только что добавил. Потому что он не знает, какую функцию он должен сейчас выполнять по вашему звонку
Фактически, если вы удалите строковый параметр из вызова, как следующий код, все компилируется правильно и работает, как раньше
источник
state
3 функции, а не одну или пару, если быть точным. На самом деле, достаточно прокомментировать любой из них, чтобы решить проблему. Лучшее совпадение среди доступных функций - это функция сparams
, вторая - функция сgenerics
параметром, когда мы добавляем третью, это создает путаницу в однойset
из функций. Думаю, это, скорее всего, непонятное сообщение об ошибке компилятора.