restricciones - ¿Por qué C#no admite clases genéricas de variantes?
no se permiten restricciones en declaraciones no genericas (2)
Una clase necesitaría contener solo los parámetros del método de salida (para ser covariante) y solo los parámetros del método de entrada (para ser contravariante). El punto es que es difícil garantizar que para las clases: por ejemplo, la clase covariante (por parámetro de tipo T) no puede tener campos de T, porque podría escribir en esos campos. Funcionaría muy bien para clases verdaderamente inmutables, pero no hay un soporte completo para la inmutabilidad en C # en este momento (por ejemplo, como en Scala).
Esta pregunta ya tiene una respuesta aquí:
- .NET 4.0 Covariance 3 respuestas
Toma este pequeño ejemplo de LINQPad:
void Main()
{
Foo<object> foo = new Foo<string>();
Console.WriteLine(foo.Get());
}
class Foo<out T>
{
public T Get()
{
return default(T);
}
}
No se compila con este error:
Modificador de varianza no válido. Solo los parámetros de tipo interfaz y delegado pueden especificarse como variante.
No veo ningún problema lógico con el código. Todo puede ser verificado estáticamente. ¿Por qué esto no está permitido? ¿Causaría alguna inconsistencia en el lenguaje o se consideró demasiado costoso de implementar debido a una limitación en el CLR? Si es lo último, ¿qué debo saber como desarrollador acerca de dicha limitación?
Teniendo en cuenta que las interfaces lo admiten, habría esperado que el soporte de clase se siguiera lógicamente a partir de eso.
Una razón sería:
class Foo<out T>
{
T _store;
public T Get()
{
_store = default(T);
return _store;
}
}
Esta clase contiene una característica que no es covariante, porque tiene un campo, y los campos se pueden establecer en valores. Sin embargo, se utiliza de forma covariante, ya que solo se le asigna el valor predeterminado y eso solo será null
en cualquier caso en el que se utilice realmente la covarianza.
Como tal, no está claro si podríamos permitirlo. No permitirlo irritaría a los usuarios (después de todo, coincide con las mismas reglas potenciales que sugieres), pero permitirlo es difícil (el análisis se ha vuelto un poco complicado y no estamos empezando a buscar casos realmente difíciles).
Por otro lado, el análisis de esto es mucho más simple:
void Main()
{
IFoo<object> foo = new Foo<string>();
Console.WriteLine(foo.Get());
}
interface IFoo<out T>
{
T Get();
}
class Foo<T> : IFoo<T>
{
T _store;
public T Get()
{
_store = default(T);
return _store;
}
}
Es fácil determinar que ninguna de las implementaciones de IFoo<T>
rompe la covarianza, porque no tiene ninguna. Todo lo que se necesita es asegurarse de que no se utiliza T
como parámetro (incluido el de un método de establecimiento) y está hecho.
El hecho de que la restricción potencial sea mucho más ardua en una clase que en una interfaz por razones similares, también reduce el grado en que las clases covariantes serían útiles. Ciertamente, no serían inútiles, pero el balance de cuán útiles serían sobre cuánto trabajo sería especificar e implementar las reglas sobre lo que se les permitiría hacer es mucho menor que el balance de cuán útiles son las interfaces covariantes. sobre cuánto trabajo fue especificarlos e implementarlos.
Ciertamente, la diferencia es suficiente para que haya pasado el punto de "bueno, si vas a permitir X, sería una tontería no permitir Y ...".