2010-04-28 11 views
45

Si lo tenemos para interfaces, ¿por qué no lo tenemos también para clases? ¿Cuál sería el problema que incurriríamos al usarlo?¿Por qué no hay varianza genérica para las clases en C# 4.0?

Gracias

+5

publicar algunos ejemplo trivial de lo que estás hablando. No puedo pensar en un escenario donde usaría esto. –

+2

@Ian P: vea mi respuesta para ver un ejemplo. –

+0

posible duplicado de [¿Por qué C# (4.0) no permite la covarianza y la contradicción en los tipos de clases genéricas?] (Http://stackoverflow.com/questions/2541467/why-does-c-sharp-4-0-not- allow-co-and-contravariance-in-generic-class-types) – nawfal

Respuesta

95

Supongamos que tenemos una clase C<T> que fue covariante en T. Lo que podría parecerse a su puesta en práctica? T tiene que estar fuera solo. Esto significa que C<T> no puede tener ningún método que tome una T, ninguna propiedad de tipo T con un setter, ni ningún campo de tipo T, porque los campos son lógicamente los mismos que los establecedores de propiedades; T entra.

Casi todo lo que se puede construir con una clase covariante es algo inmutable en lo que se refiere a T. Ahora, creo que sería increíble tener listas y pilas inmutables covariantes y todo lo demás, tipos de clases. Pero esa característica no es tan obviamente asombrosa que justificaría claramente el gasto masivo al hacer que el sistema tipo soporte de forma nativa tipos de clases inmutables covariantes.

Un comentario anterior solicitó un ejemplo de dónde sería útil. Tenga en cuenta el siguiente boceto:

sealed class Stack<out T> 
{ 
    private readonly T head; 
    private readonly Stack<T> tail; 
    public T Peek() { return head; } 
    public Stack<T> Pop() { return tail; } 
    public Stack(T head, Stack<T> tail) 
    { 
     this.tail = tail; 
     this.head = head; 
    } 
} 
static class StackExtensions 
{ 
    public static Stack<T> Push<T>(this Stack<T> tail, T head) 
    { 
     return new Stack<T>(head, tail); 
    } 
    public static bool IsEmpty<T>(this Stack<T> stack) 
    { 
     return stack == null; 
    } 
} 

Supongamos que tiene clases covariantes. Ahora se puede decir

Stack<string> strings = null; 
strings = strings.Push("hello"); 
strings = strings.Push("goodbye"); 
Stack<object> objects = strings; 
objects = objects.Push(123); 

Y bueno, sólo empujó un entero en una pila de cadenas, pero todo salió bien! No hay ninguna razón por la cual esto no pueda ser seguro. Una operación que violaría la seguridad de tipo en una estructura de datos mutable puede ser covariante de forma segura en una estructura de datos inmutables.

+3

Gran ejemplo, Eric. Lo siento, no puedo votar dos veces. –

+3

Lo hice por ti. Ahora es una lástima que no pueda volver a votar nuevamente. –

+5

La idea de colecciones inmutables es tan útil que Scala tiene un conjunto completo de colecciones inmutables, y todas admiten la covarianza. –

2

El equipo .NET junto con el equipo C# y VB.NET tiene recursos limitados, el trabajo que han realizado en co y contravariancia resuelve la mayor parte del problema del mundo real. Los sistemas de tipo son muy complejos para hacerlo bien: una solución que funciona en el 99.9999% de los casos no es lo suficientemente buena si conduce a códigos inseguros en los demás casos.

No creo que el costo/tiempo de compatibilidad con las especificaciones de co y contravariancia (por ejemplo, "in"/"out") en los métodos de clase sea de un valor suficientemente grande. Puedo ver muy pocos casos en los que podrían ser utilizables, debido a la falta de herencia de clase multiplicada.

¿Hubiera preferido esperar otros 6 meses para .net para obtener este soporte?


Otra forma de pensar en esto es que en.netos

  • Interfaces/delegados - se utilizan para modelar el sistema tipo conceptual de una aplicación
  • Clase se utilizan para implementar los tipos anteriores
  • clase de herencia se usa para reducir la duplicación de código al hacer lo anterior
  • co y contrav Ariance es sobre el sistema tipo conceptual de una aplicación
Cuestiones relacionadas