Pergunta

Acabei de atingir uma situação em que um método despacho era ambíguo e me perguntei se alguém poderia explicar com que base o compilador (.NET 4.0.30319) escolhe qual sobrecarga chamar

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());
}

De qualquer forma, por que o compilador não me avisa ou mesmo por que compila? Muito obrigado por quaisquer respostas.

Foi útil?

Solução

Segue as regras na seção 7.5.3.2 do Especificação C# 4 ("MEMBRO DE FUNÇÃO MELHOR").

Primeiro (bem, depois de ver que ambos os métodos são aplicável) Precisamos verificar as conversões dos tipos de argumento para os tipos de parâmetros. Nesse caso, é razoavelmente simples, porque há apenas um argumento. Nenhuma conversão do tipo de argumento para o tipo de parâmetro é "melhor" porque ambos estão se convertendo de ConcreteA para IfaceA. Portanto, passa para o próximo conjunto de critérios, incluindo o seguinte:

Caso contrário, se o MP tiver tipos de parâmetros mais específicos que o MQ, o MP será melhor que o MQ. Seja {r1, r2,…, rn} e {s1, s2,…, sn} representam os tipos de parâmetros desinstaliados e não especiais de MP e MQ. Os tipos de parâmetros do MP são mais específicos que os MQs se, para cada parâmetro, o RX não é menos específico que o SX e, para pelo menos um parâmetro, o RX é mais específico que o sx: específico que o sx:

  • Um parâmetro de tipo é menos específico que um parâmetro não do tipo.
  • ...

Então, mesmo que o conversão é igualmente bom, a sobrecarga usando IfaceA diretamente (em vez de via delegados) é considerado "melhor" porque um parâmetro do tipo IfaceA é mais específico do que um parâmetro do tipo T.

Não há como fazer com que o compilador avise sobre esse comportamento - é apenas uma resolução normal de sobrecarga.

Outras dicas

Porque o compilador escolhe o mais específico primeiro.

O que acontece se você ligar assim:

void code()   
{ 
    var concreteB = new ConcreteB(); 

    IfaceA  x = concreteB.Add(new ConcreteA()); 
} 

Isso me lembra um pouco da "inferência de tipo A-go" em Jon Skeet de Branteaser. Se você não quiser confiar no compilador, convém forçar sua escolha ligando Add<ConcreteA>(new ConcreteA())

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top