Это неудачное взаимодействие между необязательными параметрами и остальным языком, и хорошая причина, по которой вам не следует перегружать методы, использующие необязательные параметры (или добавлять перегрузки в методы, которые этого не делают).Автор B
сделал что-то действительно плохое!
Вызов не является двусмысленным, потому что правила поиска метода не меняются для base
, только правила, для которых метод в конечном итоге вызываетсяпосле разрешения перегрузки - по сути, перегрузки определяются так, как если бы вызов прочитал ((C) this).Foo(0)
.Для этого вызова B.Foo(int, int, int)
считается единственным кандидатом, потому что это ближайший не override
метод при переходе по цепочке наследования - он выбирается еще до того, как мы даже рассмотрим A.Foo(int)
.Если бы A
представил метод, не было бы проблемы, так как в этом случае перегрузка с одним параметром считалась бы лучшим методом.
Если бы необязательные параметры были частью C # с самого начала, скореечем относительно позднее добавление к стороне (с немного странной реализацией, где значения раскрываются на сайте вызова), это могло бы быть рассмотрено и как-то смягчено.Так как это наиболее очевидный способ исправить это, было бы на самом деле изменить правила для поиска base
, поэтому он предпочитает сопоставлять методы, которые точно соответствуют сигнатуре метода, в котором он встречается, но это только усложнит и без того сложные правила дляРазрешение перегрузки еще больше, и оно, безусловно, может нарушить существующий код, поэтому шансы на что-то подобное случается невелики.
Если вы не можете изменить A
, B
и C
, все еще естьспособ написать D
, чтобы получить желаемое поведение, используя (если это слово) еще одну довольно неясную особенность C #, которая даже старше: группы методов!
class D : C
{
public override string Foo(int a)
{
Func<int, string> foo = base.Foo;
return foo(a);
}
}
Это однозначно вызывает C.Foo(int)
, потому чтопреобразования делегатов в группах методов не учитывают необязательные параметры, и поэтому B.Foo(int, int, int)
не является допустимым кандидатом, что вынуждает нас пойти дальше по цепочке и найти A.Foo(int)
.