Ответ 1
Это правило, и оно тебе может не понравиться...
Цитата от Эрика Липперта
если какой-либо метод в более производном классе является подходящим кандидатом, он автоматически лучше, чем любой метод в менее производном классе, даже если метод с меньшим производным имеет лучшее совпадение сигнатуры.
Причина в том, что метод (то есть лучшее совпадение сигнатур), возможно, был добавлен в более поздней версии и, таким образом, представляет сбой " хрупкого базового класса ".
Примечание: это довольно сложная/углубленная часть спецификаций С#, и она прыгает повсюду. Тем не менее, основные части проблемы, с которой вы столкнулись, написаны следующим образом
Обновить
И именно поэтому мне нравится stackoverflow, это такое прекрасное место для изучения.
Я цитировал раздел об обработке во время выполнения вызова метода. Где, поскольку речь идет о разрешении перегрузки времени компиляции, и должно быть.
7.6.5.1 Вызовы методов
...
Набор методов-кандидатов сокращен, чтобы содержать только методы из наиболее производных типов: для каждого метода CF в наборе, где C - это тип, в котором объявлен метод F, все методы, объявленные в базовом типе C, удаляются из набор. Кроме того, если C является типом класса, отличным от объекта, все методы, объявленные в типе интерфейса, удаляются из набора. (Это последнее правило влияет только тогда, когда группа методов была результатом поиска члена по параметру типа, имеющему эффективный базовый класс, отличный от объекта, и непустой эффективный набор интерфейсов.)
Пожалуйста, ознакомьтесь с ответом Эрика на fooobar.com/questions/16554502/... чтобы получить полную информацию о том, что здесь происходит, и соответствующую часть спецификаций.
оригинал
Спецификация языка С# версия 5.0
7.5.5 Вызов члена функции
...
Обработка во время выполнения вызова члена функции состоит из следующих шагов, где M - это член функции, а если M - это элемент экземпляра, E - выражение экземпляра:
...
Если M является членом функции экземпляра, объявленным в ссылочном типе:
- E оценивается. Если эта оценка вызывает исключение, дальнейшие шаги не выполняются.
- Список аргументов оценивается, как описано в §7.5.1.
- Если тип E является типом значения, для преобразования E в объект типа выполняется преобразование в бокс (§4.3.1), и E рассматривается как объект типа на следующих этапах. В этом случае M может быть только членом System.Object.
- Значение E проверяется, чтобы быть действительным. Если значение E равно нулю, выдается исключение System.NullReferenceException и дальнейшие шаги не выполняются.
- Реализация функции-члена для вызова определяется:
- Если тип времени привязки E является интерфейсом, то вызываемый элемент функции является реализацией M, предоставленной типом времени выполнения экземпляра, на который ссылается E. Этот член функции определяется путем применения правил отображения интерфейса (§13.4.4) для определения реализации M, предоставляемой типом времени выполнения экземпляра, на который ссылается E.
- В противном случае, если M является членом виртуальной функции, вызываемый элемент функции является реализацией M, предоставленной типом времени выполнения экземпляра, на который ссылается E. Этот член функции определяется путем применения правил для определения наиболее производной реализации ( §10.6.3) M относительно типа времени выполнения экземпляра, на который ссылается E.
- В противном случае M - это не виртуальный член функции, а вызываемый член функции - это сам M.
После прочтения спецификаций, что интересно, если вы используете интерфейс, описывающий метод, компилятор выберет сигнатуру перегрузки, работая в свою очередь, как и ожидалось
public interface ITest
{
void Foo(int x);
}
Который может быть показан здесь
Что касается интерфейса, то он имеет смысл при рассмотрении поведения перегрузки для защиты от базового класса хрупкого
Дополнительные ресурсы
Эрик Липперт, чем ближе, тем лучше
Аспект разрешения перегрузки в С#, о котором я хочу поговорить сегодня, действительно является фундаментальным правилом, согласно которому одна потенциальная перегрузка оценивается как лучшая, чем другая для данного сайта вызовов: ближе всегда лучше, чем дальше. Есть несколько способов охарактеризовать "близость" в С#. Давайте начнем с ближайшего и двинемся дальше:
- Метод, впервые объявленный в производном классе, ближе, чем метод, впервые объявленный в базовом классе.
- Метод во вложенном классе ближе, чем метод в содержащем классе.
- Любой метод принимающего типа ближе, чем любой метод расширения.
- Метод расширения, найденный в классе во вложенном пространстве имен, ближе, чем метод расширения, найденный в классе во внешнем пространстве имен.
- Метод расширения, найденный в классе в текущем пространстве имен, ближе, чем метод расширения, найденный в классе в пространстве имен, упомянутом в директиве using.
- Метод расширения, найденный в классе в пространстве имен, упомянутом в директиве using, где директива находится во вложенном пространстве имен, ближе, чем метод расширения, найденный в классе в пространстве имен, упомянутом в директиве using, где директива находится во внешнем пространстве имен.