2010-05-18 14 views
6

me acaba de golpear una situación en la que un despacho método era ambigua y se preguntó si alguien podría explicar en qué se basa el compilador (.NET 4.0.30319) elige lo sobrecargue llamarC# Genérico método sobrecargado despachar ambigua

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

En cualquier caso, ¿por qué el compilador no me advierte o incluso por qué compila? Muchas gracias por cualquier respuesta.

+0

¿Qué sucede si espera un valor de retorno de la llamada, es decir, 'var result = concreteB.Add (new ConcreteA());'? –

+1

Porque su Método genérico tiene un "Tipo T" de retorno y el otro es de tipo "vacío". El compilador puede diferenciar entre ellos. – KroaX

+0

el compilador se quejará de que 'var' no se puede usar, porque no puede asignar el vacío a la variable implícitamente tipada – Sebastian

Respuesta

2

Sigue las normas de la sección 7.5.3.2 de la C# 4 specification ("miembro de una mejor función").

Primero (bueno, después de ver que ambos métodos son aplicable) necesitamos verificar las conversiones de tipos de argumentos a tipos de parámetros. En este caso, es razonablemente simple porque solo hay un argumento. Ni la conversión del tipo de argumento al tipo de parámetro es "mejor" porque ambos se están convirtiendo de ConcreteA a IfaceA. Por lo tanto, se mueve a la siguiente serie de criterios, incluyendo lo siguiente:

De lo contrario, si MP tiene tipos de parámetros más específicos de MQ, a continuación, MP es mejor que MQ. Deje {R1, R2, ..., RN} y {S1, S2, ..., SN} representar los tipos de parámetros sin expandir y de MP y MQ. tipos de parámetros de MP son más específicos que de MQ si, para cada parámetro, RX no es menos específica que la SX, y, por lo menos un parámetro, RX es más específica de SX: específico de SX:

  • Un parámetro de tipo es menos específico que un parámetro que no es de tipo.
  • ...

Así que, aunque la conversión es igual de bueno, la sobrecarga de uso de IfaceA directamente (en lugar de a través de delegados) se considera "mejor" porque un parámetro de tipo IfaceA es más específica que un parámetro de tipo T.

No hay forma de que el compilador advierta sobre este comportamiento, es solo una resolución de sobrecarga normal.

+0

Ok gracias un millón. Supongo que eso significa que leeré esa especificación. Solo soy un converso de Java y tengo que usar C# para este proyecto. Aunque, de hecho, creo que es más útil, pero cualquier característica que siento agrega también algunos casos extremos más ... – Sebastian

1

Porque el compilador elige primero lo más específico.

¿Qué pasa si se llama así:

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

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

¿Pero por qué' void Add (IfaceA a) 'sería más específico? ¿Es porque no uso el resultado devuelto?Y además, ¿sabes si hay una opción de compilación para darme una advertencia? – Sebastian

+0

@sebgod: puede usar la directiva '# warning' para emitir una advertencia de compilación. –

+0

El código no se compilará si elijo 'IfaceA x = concreteB.Add (new ConcreteA());': no se puede convertir implícitamente entre IFaceA y void – Sebastian

1

Esto me recuerda un poco de la "inferencia de tipos a-go-go" en Jon Skeet's BrainTeaser. Si no desea confiar en el compilador, es posible que desee forzar su elección llamando Add<ConcreteA>(new ConcreteA())

+0

Eso es lo que mi profesor siempre me dijo, nunca confíes en tu compilador – Sebastian

Cuestiones relacionadas