Я просто поразил ситуацию, где отправка метода была неоднозначна и задалась вопросом, мог ли кто-либо объяснить, на каком основании компилятор (.NET 4.0.30319) выбирает что перегрузку звонить
interface IfaceA
{
}
interface IfaceB<T>
{
void Add(IfaceA a);
T Add(T t);
}
class ConcreteA : IfaceA
{
}
class abstract BaseClassB<T> : IfaceB<T>
{
public virtual T Add(T t) { ... }
public virtual void Add(IfaceA a) { ... }
}
class ConcreteB : BaseClassB<IfaceA>
{
// does not override one of the relevant methods
}
void code()
{
var concreteB = new ConcreteB();
// it will call void Add(IfaceA a)
concreteB.Add(new ConcreteA());
}
В любом случае, почему компилятор не предупреждает меня или даже почему он компилирует? Большое спасибо за любые ответы.
Это следует правилам раздела 7.5.3.2 спецификации C# 4 ("Лучший член функции").
Сначала (ну, после того, как мы увидели, что оба метода применимы) нам нужно проверить преобразования типов аргументов в типы параметров. В данном случае это достаточно просто, поскольку аргумент только один. Ни одно из преобразований типа аргумента в тип параметра не является "лучшим", потому что оба преобразуются из ConcreteA
в IfaceA
. Поэтому он переходит к следующему набору критериев, включая следующий:
В противном случае, если MP имеет более конкретные типов параметров, чем MQ, то MP лучше, чем MQ. Пусть {R1, R2, ..., RN} и {S1, S2, ..., SN} представляют собой неосновные и нерасширенные типы параметров MP и MQ. Типы параметров MP типы параметров являются более конкретными, чем MQ, если для каждого параметра RX не является менее специфичен, чем SX, и, по крайней мере, для хотя бы одного параметра, RX является более специфичен, чем SX:специфичен, чем SX:
- Типовой параметр менее специфичен, чем нетиповой.
- ...
Таким образом, даже если преобразование одинаково хорошо, перегрузка, использующая IfaceA
напрямую (а не через делегатов), считается "лучше", потому что параметр типа IfaceA
более специфичен, чем параметр типа T
.
Нет никакого способа заставить компилятор предупредить о таком поведении - это обычное разрешение перегрузки.
Потому что компилятор сначала выбирает наиболее конкретную.
Что произойдет, если вы позвоните так:
void code()
{
var concreteB = new ConcreteB();
IfaceA x = concreteB.Add(new ConcreteA());
}
Это несколько напоминает мне "Type inference a-go-go" в Jon Skeet's BrainTeaser.
Если вы не хотите доверять компилятору, вы можете заставить его сделать выбор, вызвав Add