2009-06-08 10 views
5

No entiendo por qué el compilador no puede resolver la sobrecarga correcta para usar aquí. (código a continuación) Solo hay una versión de Add() que es apropiada: BigFoo es un IFoo y no implementa IEnumerable donde T es un IFoo. Pero insiste en informar una ambigüedad. ¿Algunas ideas? Intenté agregar un segundo parámetro de tipo genérico: Agregar donde T: IFoo donde U: IEnumerable. Pero luego la sobrecarga se ignora por completo, incluso para uso legítimo.Sobrecarga genérica de C#: el compilador no puede determinar la llamada correcta

Sé que puedo solucionar esto con el casting y la especificación de parámetros de tipo genéricos, pero en ese punto he vencido el propósito de tener una sobrecarga. Podría cuestionar la sobrecarga, pero la semántica me parece correcta: el comportamiento que estoy implementando en mi clase es que Add() agregue el objeto al por mayor como una entrada individual en la colección. (No se supone que la segunda Agregar() para ser un AddRange().)

namespace NS 
{ 
    interface IFoo { } 

    class BigFoo : IFoo, IEnumerable<int> 
    { 
    public IEnumerator<int> GetEnumerator() 
    { 
     throw new NotImplementedException(); 
    } 
    IEnumerator IEnumerable.GetEnumerator() 
    { 
     throw new NotImplementedException(); 
    } 
    } 

    class FooContainer 
    { 
    public void Add(IFoo item) { } 
    public void Add<T>(IEnumerable<T> group) where T : IFoo { } 
    } 

    class DemoClass 
    { 
    void DemoMethod() 
    { 
     BigFoo bigFoo = new BigFoo(); 
     FooContainer fooContainer = new FooContainer(); 
     // error CS0121: The call is ambiguous between the following methods or properties: 
     // 'NS.FooContainer.Add(NS.IFoo)' and 
     // 'NS.FooContainer.Add<int>(System.Collections.Generic.IEnumerable<int>)' 
     fooContainer.Add(bigFoo); 
    } 
    } 
} 

Respuesta

6

resolución de sobrecarga genérica no toma en cuenta las limitaciones, por lo que considera la versión Add<T> sea aplicable, inferir T=int.

Ambos métodos son aplicables, y ninguno es definitivamente mejor que el otro, ya que no hay conversión entre IEnumerable<int> y IFoo. Si bien los métodos genéricos se consideran "menos específicos" que los métodos no genéricos, esto solo se vuelve relevante cuando los tipos de parámetros son idénticos después del reemplazo del tipo de argumento, que en este caso no son relevantes.

+0

Jeff Richter está de acuerdo "el compilador de C# prefiere una asociación más explícita durante un partido genérico" Display ("Jeff") se correspondería con Display (String) Visualización (T) sólo se – Gishu

+1

Las reglas de desempate aplican si los tipos de parámetros formales son IDENTICOS. Por ejemplo, si tiene M (int x) y M (T t), entonces el primero es mejor que M (int t). –

+0

Ah, gracias Eric. Es bueno tener la especificación en línea y contribuir;) editará apropiadamente. –

0

El compilador debe ser lo suficientemente inteligente como para reconocer que BigFoo no se puede convertir a IEnumerable<IFoo>, pero no lo es. Simplemente ve que es IEnumerable<T>, y considera que es un posible candidato de sobrecarga (aunque la restricción impuesta que usted definió exige que T debe ser IFoo y int no se puede convertir a IFoo). Si bien es un inconveniente, no es gran cosa. Sólo fundido bigFoo a IFoo y el compilador será feliz:

fooContainer.Add((IFoo)bigFoo); 

alternativa, puede hacer que su sobrecarga genérica de Añadir más feas:

public void Add<T, U>(U group) 
    where T : IFoo 
    where U : IEnumerable<T> 
{ 
} 

De cualquier manera, usted tiene más de mecanografía, el segundo elimina la solución la necesidad de emitir llamadas a Add, pero usted tendrá que declarar explícitamente el tipo de llamadas al complemento genérico (que termina siendo más código:

fooContainer.Add<IFoo, IEnumerable<IFoo>>(enumerableFoo); 
1

En FooContainer, en el segundo "Agregar" está restringiendo que T sea del tipo IFoo. BigFoo implementa la interfaz IFoo, por lo tanto, coincide con esa definición de Agregar (aunque en realidad no lo hace, porque no implementa IEnumable <IFoo>).

no estoy seguro de entender completamente lo que quiere, pero sospecho que es la siguiente:

public void Add<T>(T group) where T : IEnumerable<IFoo> { } 

que permitirá añadir cualquier objeto T, donde T es un conjunto enumerable de objetos IFoo.

¿Eso es lo que querías?

Saludos, Richard

+0

restringir T a IEnumerable en lugar de IFoo aún no ayuda con la resolución de sobrecarga. Como dijo Jon Skeet, el compilador (desafortunadamente) no aceptará las limitaciones de la cuenta. –

+0

Ah ... Genial. Aprendí algo (otro) nuevo hoy. :-) Gracias por tomarse el tiempo para comentar: realmente pensé que el compilador consideraría las restricciones en la cláusula "where", incluso si no se molestara en considerarlas en otra parte de la firma del método. –

0

El problema aquí es que las restricciones de tipo genérico son completamente ignorado por el compilador (que sólo se basa en los tipos de parámetros). En lo que respecta al compilador, el argumento IEnumerable<T> que se aprueba podría ser IEnumerable<IFoo>. Para obtener información completa sobre este tema, consulte la sección 25.6.4 Inferencia de argumentos de tipo del C# Language Specification Tenga en cuenta que no se menciona la utilización de restricciones de tipo.

+0

Ese no es el lugar relevante para buscar las reglas de especificación que son pertinentes a esta pregunta. La sección relevante en la especificación 3.0 es 7.5.5.1, el bit que comienza "se realiza la validación final del mejor método elegido". Como puede ver en esta sección, la verificación de violaciones de restricciones se realiza DESPUÉS de la verificación de la exclusividad del conjunto candidato. –

+0

Ah, cierto. Sin duda, esa sección de la especificación al menos sugiere que las restricciones de tipo genérico * no * están teniendo en cuenta durante el proceso de inferencia, sin embargo? – Noldorin

+0

De hecho, también es el caso de que las restricciones no se utilizan durante la inferencia. –

0

Interesante .... Acabo de probar su muestra. Los genéricos continúan manteniéndome alerta.

//1 - First preference 
public void Add(BigFoo item) { Console.WriteLine("static BigFoo type Add"); } 
//2 - Second Preference 
public void Add<T>(T item) { Console.WriteLine("Generic Add"); } 
//3 - Third preferences 
public void Add(IFoo item) { Console.WriteLine("static IFoo interface Add"); } 
//4 - Compiles if 1-4 exist. Compile error (ambiguity) if only 3-4 exist. Compile error (cannot convert int to IFoo) if only 4 exists 
public void Add<T>(IEnumerable<T> group) where T : IFoo { } 
Cuestiones relacionadas