Пожелание: скрытие устаревшего метода другой перегрузкой с использованием параметров по умолчанию

Пожалуйста, подождите, это не (совсем!) Дубликат любого из этих ответов SO:

При попытке «повлиять» на то, какая перегрузка будет выбрана компилятором с учетом конфликтов, возникающих из-за необязательных параметров, ответы в приведенных выше сообщениях ссылаются на Руководство по программированию на C #, в котором указана эвристика разрешения перегрузки (ИЛИ), которая используется здесь:

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

Справедливо. Мой вопрос: почему (или почему не может) атрибут Obsolete (или какая-либо другая разметка) не влияет на решение OR при оценке двух кандидатов как одинаково хороших? Например, рассмотрим следующие перегрузки:

[Obsolete(“This method is deprecated.”)]
[EditorBrowsable(EditorBrowsableState.Never)]]
bool foo() { return true; }

bool foo(bool optional = false) { return optional; }

Кажется, что OR не должен оценивать эти перегрузки как одинаково хорошие - нерекомендуемая перегрузка с необязательным параметром должна победить. Если бы это было так в этом чрезмерно упрощенном примере, код, ранее скомпилированный для foo (), был бы обратно совместим и, к счастью, продолжал бы возвращать истину. Будущий код, скомпилированный для этой библиотеки, также может вызывать foo (), но эта разрешенная перегрузка вернет false.

Это ценная / возможная функция, которую нам не хватает в языке? Или есть какой-нибудь другой способ заставить это мое желание работать? Спасибо за любые идеи,

-Майк


person miesch1    schedule 05.05.2016    source источник


Ответы (3)


Это интересный вопрос. Тем не менее, я предпочитаю такой способ по следующим причинам:

Простота

ИЛИ зависит от сигнатуры метода, скомпилированной в модуль, и при этом интуитивно понятно. В зависимости от каких-либо атрибутов область зависимости изменится на более широкую и начнется рост снежного кома - стоит ли рассматривать, возможно, другой атрибут? Или атрибуты аргументов и т. Д.

Управление изменениями

Сигнатуры методов и ИЛИ представляют другое беспокойство, чем атрибут маркера устаревания. Последние являются метаданными, и если они начнут влиять на ИЛИ с одного момента, то это, возможно, сломает многие существующие приложения. Особенно библиотеки, у которых цикл устаревания по какой-то причине длиннее.

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

person Imre Pühvel    schedule 05.05.2016
comment
Хорошие моменты. Я бы не хотел, чтобы существующие приложения с похожими подписями начали вести себя по-другому. Это означает, что должен быть определен новый атрибут, который звучит как начало пути снежного кома, о котором вы упомянули. Ну что ж. - person miesch1; 06.05.2016

Разрешение перегрузки - очень сложная вещь, и разработчики языка долго и усердно думают о том, какими именно должны быть правила. Чем больше различных соображений и особых случаев вы добавите, тем вероятнее, что пользователи языка столкнутся с неясными подводными камнями, где он работает не совсем так, как они ожидали. Я не верю, что добавление этого нового условия для разрешения перегрузки было бы ценным дополнением к языку.

Если OR предпочитает метод без ObsoluteAttribute, то единственный способ вызвать устаревший метод без отражения будет примерно таким:

((Action)obj.foo)(); 

И если бы это был общий метод, не было бы возможности вызвать его с параметром анонимного типа. Это определенно было бы неожиданным поведением (по крайней мере, для меня).

ObsoleteAttribute (объявленный с помощью IsError = false) - это способ указать вашим потребителям, как обновить их код, не удаляя полностью предыдущие возможности. Обычно вы хотите сделать это, если планируете удалить эту функцию в будущей версии. Если вы хотите полностью запретить им вызывать этот метод, вы можете:

  1. Установите IsError = true на [Obsolete("This method is deprecated.", true)], чтобы при повторной компиляции кода генерировалась ошибка, а не предупреждение. Его все еще можно было вызвать через отражение.
  2. Полностью удалите устаревшую функцию. Его нельзя вызвать через отражение.
person p.s.w.g    schedule 05.05.2016
comment
Спасибо за содержательный ответ. Ваш ответ хорошо известен, и я уверен, что это принесет гораздо больше ошибок, чем пользы. Вы сделали замечательный вывод, единственный способ полностью скрыть устаревшую функцию - полностью удалить ее, независимо от того, работает ли ИЛИ так, как я хотел. К сожалению, для меня это не вариант. - person miesch1; 06.05.2016

В общем, имея такой код ...

[Obsolete(“This method is deprecated.”)]
bool foo() { return true; }

bool foo(bool optional = false) { return optional; }

воняет. Компилятор будет применять алгоритм для выбора правильного метода при вызове foo(), но любой разработчик, просматривающий код, должен будет знать (и применять) этот алгоритм, чтобы понять, что делает код, поэтому ремонтопригодность кода сильно снизится.

В этом случае просто удалите необязательное, так как оно причиняет больше боли, чем что-либо еще.

person Ignacio Soler Garcia    schedule 05.05.2016
comment
Спасибо за ответ. Я понимаю вашу точку зрения. Я намерен скрыть исходную перегрузку без параметров от разработчиков (в моем примере это дополнительный атрибут EditorBrowsable). Имея большую часть моей существующей документации API, иллюстрирующей использование 'foo ()', было бы неплохо реализовать решение только с прямым переадресацией, которое вызывает перегрузку необязательного параметра, но с той же подписью. Еще раз спасибо. - person miesch1; 06.05.2016
comment
@ miesch1 - Вы можете изменить реализацию устаревшего метода foo для внутреннего вызова правильного метода foo. Тогда функционально уже не будет иметь никакого значения, к какому метору ИЛИ разрешается. Предполагая, что вопрос касается только подписи и не связан с изменением логики. - person Imre Pühvel; 06.05.2016