listas genericas clases c# generics covariance contravariance

genericas - interface c#



¿Por qué las interfaces genéricas no son co/contravariant por defecto? (2)

Por ejemplo, la IEnumerable<T> :

public interface IEnumerable<out T> : IEnumerable { IEnumerator<T> GetEnumerator(); }

En esta interfaz, el tipo genérico se usa solo como un tipo de método de interfaz de retorno y no se usa como un tipo de argumentos de método, por lo que puede ser covariante. Dando esto, ¿el compilador no puede inferir teóricamente la varianza de la interfaz? Si puede, ¿por qué C # nos obliga a establecer explícitamente las palabras clave de co / contravarianza?

Actualización : Como Jon Skeet mencionó esta pregunta, se puede escupir en sub-preguntas:

  1. ¿Puede el compilador inferir la covarianza / contravarianza del tipo genérico según cómo se usa dentro del tipo genérico actual y todos sus tipos base?

    Por ejemplo ... ¿Cuántos parámetros de interfaz genéricos de .NET Framework 4.0 se pueden marcar como co / contravariant automáticamente sin ninguna ambigüedad? ¿Cerca del 70%, 80%, 90% o 100%?

  2. Si puede, ¿ debería aplicar co / contravarianza a tipos genéricos por defecto? Al menos para aquellos tipos que es capaz de analizar e inferir co / contravarianza del tipo de uso.


Bueno, hay dos preguntas aquí. En primer lugar, ¿ podría el compilador siempre hacerlo? En segundo lugar, ¿ debería (si puede)?

Para la primera pregunta, voy a referirme a Eric Lippert, quien hizo este comentario cuando mencioné exactamente este problema en la 2ª edición de C # en profundidad:

No me queda claro que podamos razonablemente aunque quisiéramos. Podemos idear fácilmente situaciones que requieran un análisis global costoso de todas las interfaces en un programa para resolver las variaciones, y podemos idear fácilmente situaciones en las que sea <in T, out U> o <out T, in U> Y no hay manera de decidir entre ellos. Con mal desempeño y casos ambiguos es una característica poco probable.

(Espero que a Eric no le moleste que cite esto al pie de la letra; anteriormente él ha sido muy bueno compartiendo tales ideas, así que estoy en una forma pasada :)

Por otro lado, sospecho que todavía hay casos en los que se puede inferir sin ambigüedad, por lo que el segundo punto sigue siendo relevante ...

No creo que deba ser automático, incluso cuando el compilador puede saber sin ambigüedades que es válido de una sola manera. Si bien expandir una interfaz siempre es un cambio importante en cierta medida, generalmente no lo es si usted es el único que lo implementa. Sin embargo, si las personas confían en que su interfaz sea una variante, es posible que no pueda agregarle métodos sin romper clientes ... incluso si solo son personas que llaman, no implementadores. Los métodos que agregue pueden cambiar una interfaz previamente covariante para que se convierta en invariante, momento en el cual interrumpe a quienes llaman y están tratando de usarla de forma covariante.

Básicamente, creo que está bien exigir que esto sea explícito: es una decisión de diseño que deberías tomar conscientemente, en lugar de simplemente terminar accidentalmente con la covarianza / contravarianza sin haberlo pensado.


Este artículo explica que hay situaciones que el compilador no puede inferir y, por lo tanto, le proporciona la sintaxis explícita:

interface IReadWriteBase<T> { IReadWrite<T> ReadWrite(); } interface IReadWrite<T> : IReadWriteBase<T> { }

¿Qué inferes aquí in o out , ambos trabajan?