2012-03-08 13 views
5

Es en parte curiosidad y en parte porque estaba tratando de usar esto. Si tiene las siguientes definiciones, el compilador no lo permite porque dice que el miembro ya está definido. ¿Cuál es el razonamiento detrás de no permitir sobrecargas exclusivas de parámetros de tipo genérico?¿Sobrecarga de parámetros de tipo genérico no permitidos?

void Get<T>() where T: struct {} 
void Get<T>() where T: class {} 

Me parece que no hay un problema inherente con esto. Se podría argumentar que no siempre está claro qué el compilador debería elegir en los casos en que las definiciones se superponen (pero una resolución común parece ser la más específica).

¿Alguien me puede ayudar a entender o señalar a un recurso cuál es el razonamiento detrás de no permitir esto?

+0

Fuera de interés, ¿qué tipo de cosas intentaba hacer su caso de uso real? – AakashM

+0

@AakashM: simple, necesitaba convertir tipos que aceptan nulos de una manera diferente a los tipos de valores que no admiten nulos. Pensé que una sobrecarga genérica era una manera rápida de solucionarlo. El método toma un objeto y un tipo param y no puedo restringir el tipo param (bueno, puedo, pero no puedo sobrecargarlo). – Abel

Respuesta

9

Eric Lippert ya ha respondido éste, en un blog de las limitaciones genéricas y firmas de método: http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx

restricciones sobre los tipos genéricos no son parte de firmas de método en el CLR, por lo tanto, no se puede tener dos métodos que diferir solo en restricciones de tipo genérico. Sin el soporte de CLR, sería bastante complicado conseguir que C# los admita de manera sensata y compatible con otros lenguajes .NET.

+0

Si lo dice de esa manera, parece lógico. Uno solo puede preguntarse, ¿por qué no se hizo parte de la firma del método? ¿Tenía Anders Hejlsberg una buena razón para no diseñarlo de otra manera? Todavía no leí completamente el artículo de Lippert. Gracias por el puntero. – Abel

+1

Supongo que es una limitación de CLR: CLR tampoco considera las restricciones de tipo genérico como parte de una firma de método. No hay forma de representar restricciones genéricas en la representación binaria de las firmas de métodos, por lo que es una limitación bastante profunda. – thecoop

+4

@Abel: thecoop es correcto; esto es una limitación del CLR. Podríamos, por supuesto, en teoría hacer que las restricciones formen parte de la firma, lo que ciertamente sería útil en su caso específico. Pero en el caso general, presenta muchos problemas.Por ejemplo: 'void M () donde T: IFoo {}' y 'void M () donde T: IBar {}' serían diferentes firmas de método; Entonces, ¿qué método es 'M ' donde 'Blah' es una clase que implementa tanto' IFoo' como 'IBar'? Las reglas para manejar firmas ambiguas ya son bastante complicadas; no agreguemos un nuevo conjunto de maneras de ser ambiguos. –

1

La restricción struct en Nullable<T> es en mi humilde opinión realmente desafortunado. Algo como un Nullable<String> o un Nullable<Nullable<Nullable<int>>> puede ser un desperdicio, pero ¿y qué? Box el primero como su contenido; desempaquetarlo como contenido y establecer HasValue si el contenido no es nulo. Escriba el primero como int si todos los elementos nulos informan HasValue, y cuando desempaquete, establezca HasValue de todos los elementos anidados si el contenido no fue nulo.

De lo contrario, le sugiero que cree una clase genérica estática con el parámetro de tipo T que contiene una propiedad de delegado que acepta un T como parámetro. La propiedad debe devolver el contenido de un campo privado que debe inicializarse para apuntar a un método que verificará el tipo de T y establecerá el delegado en la versión struct o class, según corresponda.

Aquí hay una muestra de lo que estoy hablando; éste usa varias restricciones de interfaz en lugar de restricciones de estructura/clase, pero los mismos principios se pueden usar con la misma eficacia.

 
     static class _FooDispatcher<T> 
     { 
      public static Action<T> Foo = setupFoo; 

      static void doFooWithIGoodFoo<TT>(TT param) where TT : IGoodFoo 
      { 
       Console.WriteLine("Dispatching as IGoodFoo with {1} type {0}", typeof(TT).ToString(), typeof(TT).IsValueType ? "value" : "reference"); 
       param.Foo(); 
      } 
      static void doFooWithIOkayFoo<TT>(TT param) where TT : IOkayFoo 
      { 
       Console.WriteLine("Dispatching as IOkayFoo with {1} type {0}", typeof(TT).ToString(), typeof(TT).IsValueType ? "value" : "reference"); 
       param.Foo(); 
      } 
      static void doFooSomehow<TT>(TT param) 
      { 
       Console.WriteLine("Nothing exciting with {1} type {0}", typeof(TT).ToString(), typeof(TT).IsValueType ? "value" : "reference"); 
      } 
      static void setupFoo(T param) 
      { 
       System.Reflection.MethodInfo mi; 
       if (typeof(IGoodFoo).IsAssignableFrom(typeof(T))) 
        mi = typeof(_FooDispatcher<T>).GetMethod("doFooWithIGoodFoo", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); 
       else if (typeof(IOkayFoo).IsAssignableFrom(typeof(T))) 
        mi = typeof(_FooDispatcher<T>).GetMethod("doFooWithIOkayFoo", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); 
       else 
        mi = typeof(_FooDispatcher<T>).GetMethod("doFooSomehow", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); 
       Foo = (Action<T>)(@Delegate.CreateDelegate(typeof(Action<T>), mi.MakeGenericMethod(typeof(T)))); 
       Foo(param); 
      } 
     } 
Cuestiones relacionadas