example - parámetro genérico covariante c#
remarks c# (3)
Si estamos hablando de varianza genérica:
La covarianza se trata de devolver los valores de una operación a la persona que llama.
Contravarianza Es opuesto y se trata de valores pasados por la persona que llama:
Por lo que sé, si un parámetro de tipo solo se usa para la salida, puede usar out. Sin embargo, si el tipo solo se utiliza como entrada, puede usar in. Es la conveniencia porque el compilador no puede estar seguro de si puede recordar qué forma se llama covarianza y cuál se llama contravariancia. Si no los declara explícitamente una vez que se ha declarado el tipo, los tipos relevantes de conversión están disponibles implícitamente .
No hay varianza (ya sea covarianza o contravarianza) en las clases porque incluso si tiene una clase que solo usa el parámetro de tipo para la entrada (o solo lo usa para la salida), no puede especificar los modificadores de entrada o salida. Solo las interfaces y los delegados pueden tener parámetros de tipo de variante. En primer lugar, el CLR no lo permite. Desde el punto de vista conceptual Las interfaces representan una forma de ver un objeto desde una perspectiva particular, mientras que las clases son tipos de implementación más reales .
Intento entender esto, pero no obtuve ningún resultado apropiado de la búsqueda.
En c # 4, puedo hacer
public interface IFoo<out T>
{
}
¿Cómo es esto diferente de
public interface IFoo<T>
{
}
Todo lo que sé es que la out
hace que el parámetro genérico sea covariante (??). ¿Puede alguien explicar el uso de la parte <out T>
con un ejemplo? ¿Y también por qué es aplicable solo para interfaces y delegados y no para clases?
Disculpe si es un duplicado y ciérrelo como tal si lo es.
Significa que si tienes esto:
class Parent { }
class Child : Parent { }
Entonces, una instancia de IFoo<Child>
también es una instancia de IFoo<Parent>
.
¿Alguien puede explicar el uso de la parte de salida T con un ejemplo?
Por supuesto. IEnumerable<T>
es covariante. Eso significa que puedes hacer esto:
static void FeedAll(IEnumerable<Animal> animals)
{
foreach(Animal animal in animals) animal.Feed();
}
...
IEnumerable<Giraffe> giraffes = GetABunchOfGiraffes();
FeedAll(giraffes);
"Covariante" significa que la relación de compatibilidad de asignación del argumento de tipo se conserva en el tipo genérico . Giraffe
es una asignación compatible con Animal
, y por lo tanto esa relación se conserva en los tipos construidos: IEnumerable<Giraffe>
es una asignación compatible con IEnumerable<Animal>
.
¿Por qué es aplicable solo para interfaces y delegados y no para clases?
El problema con las clases es que las clases tienden a tener campos mutables. Tomemos un ejemplo. Supongamos que permitimos esto:
class C<out T>
{
private T t;
OK, ahora piensa esta pregunta detenidamente antes de continuar. ¿Puede C<T>
tener algún método fuera del constructor que establezca el campo t
en un valor distinto al predeterminado?
Debido a que debe ser seguro, C<T>
ahora no puede tener métodos que tomen una T como argumento; T solo se puede devolver. Entonces, ¿quién establece t, y de dónde obtienen el valor que le asignaron ?
Los tipos de clases covariantes realmente solo funcionan si la clase es inmutable . Y no tenemos una buena manera de hacer clases inmutables en C #.
Ojalá lo tuviéramos, pero tenemos que vivir con el sistema tipo CLR que nos dieron. Espero que en el futuro podamos tener un mejor soporte para las clases inmutables y para las clases covariantes.
Si esta característica le interesa, considere leer mis largas series sobre cómo diseñamos e implementamos la función. Comienza desde abajo:
https://blogs.msdn.microsoft.com/ericlippert/tag/covariance-and-contravariance/