2011-12-14 18 views
12
class Poly 
    { 
    public static void WriteVal(int i) { System.Console.Write("{0}\n", i); } 
    public static void WriteVal(string s) { System.Console.Write("{0}\n", s); } 
    } 

class GenWriter<T> 
    { 
     public static void Write(T x) { Poly.WriteVal(x); } 
    } 

¿Por qué el método inocente (para el programador C++) no es aceptable en C#?Polimorfismo, sobrecargas y genéricos en C#

Se puede ver que el compilador intenta hacer coincidir el tipo de parámetro T para sobrecargas concretas antes de instancias:

de error 3 El partido mejor método sobrecargado para 'TestGenericPolyMorph.Poly.WriteVal (int)' tiene algún argumentos inválidos

Por supuesto. el propósito no era usar el método estático como se indica anteriormente, la intención es crear un contenedor con comportamiento polimórfico. Nota: utilizo VS 2010.

Tenga en cuenta que toda la información necesaria está disponible en tiempo de compilación. Una vez más: el problema es que la validación se realiza antes de la instanciación de la plantilla.

adición después de la discusión:

Bueno, puede ser que no he insistido en este cabo correctamente. La pregunta no era solo acerca de la diferencia entre genéricos y plantillas, sino también sobre la solución del siguiente problema: dado un conjunto de sobrecargas que abordan diferentes tipos, quiero generar un conjunto de clases contenedoras que proporcionen método virtual (polimorfismo) para estos tipos. El precio de resolución de los métodos virtuales en tiempo de ejecución es mínimo y no afecta el rendimiento. Aquí es donde las plantillas de C++ son útiles. Obviamente, la sobrecarga de la resolución de tipo de tiempo de ejecución para dinámico es bastante diferente. Entonces, la pregunta es si se pueden convertir las sobrecargas existentes a polimorfismo sin replicar el código y sin pagar la penalización de rendimiento (p. Ej., No estoy seguro de lo que gano con dinámico en comparación con "cambiar" intentando fundir excepto de mejor sintaxis).

Una de las soluciones que he visto hasta ahora era generar/emitir código (sic!), Es decir, en lugar de cortar y pegar para hacerlo automáticamente.

Por lo tanto, en lugar del procesamiento de la plantilla de C++ simplemente lo hacemos manualmente o simplemente reinventamos el procesador de macro/plantilla.

¿Algo mejor?

+0

"El problema es que la validación se realiza antes de la instanciación de la plantilla". - Si eso es verdad. No puede hacer eso en C# porque el compilador no sabe cuál es el tipo de x en tiempo de compilación. – TheBoyan

+1

posible duplicado de [¿Cuáles son las diferencias entre los genéricos en C# y Java ... y las plantillas en C++?] (Http://stackoverflow.com/questions/31693/what-are-the-differences-between-generics-in -c-sharp-and-java-and-templates-i) –

Respuesta

5

Por qué no puedes simplemente escribir:

public static void Write<T>(T x) { System.Console.Write("{0}\n", x); } 
+0

Porque en tales situaciones (puede no ser en este caso) polimorfismo ya existente: x.ToString() se define para todos los tipos relevantes. Estoy tratando de ajustar las clases sin métodos verdaderamente virtuales (pero con sobrecargas bien definidas) con clases que proporcionan un método virtual. –

3

El C++ y C# genéricos son diferentes (http://msdn.microsoft.com/en-us/library/c6cyy67b(v=VS.80).aspx, búsqueda de "diferencia de C# C++ genéricos" en su sitio de búsqueda favorito)

corta: C# el compilador debe crear una clase completa de GenWriter<T> con todas las coincidencias de tipo simplemente mirando la clase en sí. Por lo tanto, no sabe si T solo será int/cadena o cualquier otro tipo.

El compilador de C++ crea una clase real al observar la instanciación del genérico GenWriter<int> y la declaración GenWriter<T> y luego crea una clase para esa instancia en particular.

27

Respuesta corta:

C# genéricos no son las plantillas de C++; a pesar de su sintaxis similar, son bastante diferentes.Las plantillas se crean en tiempo de compilación, una vez por creación de instancias, y el código de plantilla debe ser correcto para , solo los argumentos de plantilla proporcionados en realidad. Las plantillas realizan tareas como resolución de sobrecarga y análisis de tipo una vez por instanciación; Básicamente son un mecanismo inteligente de "búsqueda y reemplazo" en , texto del código fuente.

C# genéricos son realmente tipos genéricos; deben ser correctos para cualquier posible argumento de tipo. El código genérico se analiza una vez, la resolución de sobrecarga se realiza una vez, y así sucesivamente.

Respuesta larga: Este es un duplicado de

What are the differences between Generics in C# and Java... and Templates in C++?

Ver las respuestas largas allí para obtener más detalles.

Véase también mi artículo sobre el tema:

http://blogs.msdn.com/b/ericlippert/archive/2009/07/30/generics-are-not-templates.aspx

+2

excelente respuesta ... como siempre :) – TheBoyan

1

Si alguien fue llamar GenWriter(5.0), esto se infiere que GenWriter<double>(5.0), y la llamada al método dentro Write(T x) se convertiría en:

public static void Write(double x) { Poly.WriteVal(x); } 

No hay sobrecarga de WriteVal que toma un doble. El compilador le informa que no hay sobrecargas válidas de WriteVal.

Los genéricos C# y las plantillas C++ no son del todo equivalentes.

0

No puede hacer eso en C# porque el compilador no sabe cuál es el tipo de x en tiempo de compilación.

Sin conocimiento del tipo real de T, al compilador le preocupa que haya intentado realizar una conversión personalizada. La solución más simple es usar el operador as, que no es expresivo porque no puede realizar una conversión personalizada.

Una solución más gereral es lanzar al objeto primero. Esto es debido a problemas atento unboxing de boxeo:

return (int)(object) x; 

Tenga en cuenta que C# genéricos no son como las plantillas de C++. Las plantillas C++ son piezas de código que se compilan para cada tipo por separado. Mientras que los genéricos de C# se compilan en una assebmly.