2010-02-23 25 views
6

Voy a crear una clase personalizada genérica:tipos C# genéricos causa ambigüedad

class Widget< T1, T2> 
{ 
    ... 
    public bool Bar(T1 type1) 
    { 
     ... 
    } 
    public bool Bar(T2 type2) 
    { 
     ... 
    } 
    ... 
} 

Las siguientes líneas, por supuesto, crea un error ambigua llamada de compilación:

Widget<int, int> Foo = new Widget<int, int>(); 
... 
Foo.Bar(5); 
... 

¿Hay alguna forma en torno a este ? ¿Hay una cláusula que pueda poner en las líneas de "donde: TypeOf (T1)! = TypeOf (T2)", o cualquier otra forma de hacer que esto no sea ambiguo? Preferiblemente int, int estaría disponible, pero no es manditory.

Actualización:

En realidad en mi propia descubrieron una solución aceptable (para mí) a este problema, para aquellos que estén interesados ​​

class Widget< T1, T2> 
{ 
    ... 
    public bool Bar(object o) 
    { 
     if(o.GetType() == typeof(T1)) 
     { 
      ... 
     } 
     if(o.GetType() == typeof(T2)) 
     { 
      ... 
     } 
    } 
    ... 
} 
+0

Me pregunto cuál es el caso del mundo real para que ... – user76035

+0

en realidad se convierta en un método no válido sobre la carga – Perpetualcoder

+1

@wwosik: Un diccionario con 2 teclas. –

Respuesta

18

Is there a clause that I can put along the lines of "where : TypeOf(T1) != TypeOf(T2)"

Puede hacer que su constructor genere una excepción en el tiempo de ejecución. Pero no hay forma de evitar esta situación en tiempo de compilación.

Any way to make this unambiguous?

Debe cambiar los nombres de sus métodos para que no colisionen. Esa es la forma más segura y fácil de hacer.

De hecho, IIRC la CLR se reserva el derecho de no crear un tipo que produzca una ambigüedad en las firmas de métodos como esa. (Obviamente, nuestra implementación realmente tiene éxito, pero estás pisando hielo muy delgado cuando sacas este tipo de travesuras.)

Hacer este tipo de cosas es una muy, muy mala idea porque te puede llevar a todo tipo de problema. Aquí está un ejemplo de cómo van mal las cosas:

http://blogs.msdn.com/ericlippert/archive/2006/04/05/odious-ambiguous-overloads-part-one.aspx

http://blogs.msdn.com/ericlippert/archive/2006/04/06/odious-ambiguous-overloads-part-two.aspx

También tenga en cuenta que el compilador se le impida la creación de un tipo tal que implementa dos interfaces de que podrían ser idénticos en construcción. Esto es ilegal:

class C<T, U> : IFoo<T>, IFoo<U> { ... } 

porque entonces podría construir C<int, int> y el CLR no tendría forma de saber qué métodos que se correspondían con ranuras de interfaz.

Pero parece que me he desviado un poco. De vuelta al tema.

Como usted es el creador de esta clase, puede optar por cambiar el nombre de sus métodos de "Barra" para que sean diferentes en cualquier construcción posible. Supongamos que obstinadamente eliges no hacerlo. ¿Hay algo que el usuario de su desafortunada clase pueda hacer si quiere hacer Widget<int, int>? Sí, en realidad, hay, como señala kvb. Pueden definir métodos de extensión que hagan lo correcto.

public static void BarTheFirst<A, B>(this Widget<A, B> w, A a) 
{ 
    w.Bar(a); 
} 

public static void BarTheFirst<A, B>(this Widget<A, B> w, B b) 
{ 
    w.Bar(b); 
} 

resolución de sobrecarga se realiza en tiempo de compilación, y en tiempo de compilación todo lo que sabemos es que el primero llama a la barra que tiene una "A", y la segunda llama la barra que tiene un " SEGUNDO". No volver a hacer resolución de sobrecarga en tiempo de ejecución, por lo que ahora podemos decir

Widget<int, int> w = whatever; 
w.BarTheFirst(5); 
w.BarTheSecond(10); 

y va a hacer lo correcto.

+0

"De hecho, IIRC, el CLR se reserva el derecho de no crear un tipo que produzca una ambigüedad en las firmas de métodos como esa". ¿Seriamente? Si ese es el caso, ¿no debería el compilador de C# escupe una advertencia (al menos) al crear 'Widget '? –

+0

Bueno, lo hace: ver OP: "error de compilación de llamadas ambiguas" – user76035

+0

@wwosik: Ese error es para la llamada al método. Eric está hablando de que CLR explota el programa en el momento en que ese tipo ** se creó **. De hecho, cuando lo pienso, no es fácil para el compilador descubrirlo cuando está profundamente anidado en métodos genéricos como, por ejemplo, ensamblajes externos. –

5

Dé a sus funciones nombres únicos

0

Esto parece un caso en el que quiera una clase genérica de base

abstract class WidgetBase<T1> 
{ 

    public abstract bool Bar(T1 type); 
    ... 
} 

Entonces heredar para su aplicación

class WidgetA : WidgetBase<int> 
{ 
    public bool Bar(int type) 
    { 
     ... 
    } 
} 

class WidgetB : WidgetBase<int> 
{ 
    public bool Bar(int type) 
    { 
     ... 
    } 
} 
1

Estoy de acuerdo con otros que usted debe cambiar su clase para que la colisión no ocurre (por ejemplo, al cambiar el nombre de los métodos). Sin embargo, si usted no es dueño de la clase, no es una solución:

public static class WidgetExtensions 
{ 
    public static bool Bar1<T1,T2>(this Widget<T1, T2> w, T1 t1) { return w.Bar(t1); } 
    public static bool Bar2<T1,T2>(this Widget<T1, T2> w, T2 t2) { return w.Bar(t2); } 
} 

Puede utilizar estos métodos de extensión para llamar a la adecuada Bar sobrecarga. Los métodos estáticos no necesitan ser métodos de extensión, pero creo que lo hace un poco más claro.