Covariant generic parameter
I'm trying to understand this but I didn't get any appropriate results from searching.
In C# 4, I can do
public interface IFoo<out T>
{
}
How is this different from
public interface IFoo<T>
{
}
All I know is the out
makes the generic parameter covariant (??).
Can someone explain开发者_StackOverflow the usage of <out T>
part with an example? And also why is applicable only for interfaces and delegates and not for classes?
Sorry if it's a duplicate and close it as such if it is.
Can someone explain the usage of the out T part with an example?
Sure. IEnumerable<T>
is covariant. That means you can do this:
static void FeedAll(IEnumerable<Animal> animals)
{
foreach(Animal animal in animals) animal.Feed();
}
...
IEnumerable<Giraffe> giraffes = GetABunchOfGiraffes();
FeedAll(giraffes);
"Covariant" means that the assignment compatibility relationship of the type argument is preserved in the generic type. Giraffe
is assignment compatible with Animal
, and therefore that relationship is preserved in the constructed types: IEnumerable<Giraffe>
is assignment compatible with IEnumerable<Animal>
.
Why is applicable only for interfaces and delegates and not for classes?
The problem with classes is that classes tend to have mutable fields. Let's take an example. Suppose we allowed this:
class C<out T>
{
private T t;
OK, now think this question through carefully before you go on. Can C<T>
have any method outside of the constructor that sets the field t
to something other than its default?
Because it must be typesafe, C<T>
can now have no methods that take a T as an argument; T can only be returned. So who sets t, and where do they get the value they set it from?
Covariant class types really only work if the class is immutable. And we don't have a good way to make immutable classes in C#.
I wish we did, but we have to live with the CLR type system that we were given. I hope in the future we can have better support for both immutable classes, and for covariant classes.
If this feature interests you, consider reading my long series on how we designed and implemented the feature. Start from the bottom:
https://blogs.msdn.microsoft.com/ericlippert/tag/covariance-and-contravariance/
If we're talking about generic variance:
Covariance is all about values being returned from an operation back to the caller.
Contravariance It’s opposite and it's about values being passed into by the caller:
From what I know if a type parameter is only used for output, you can use out. However if the type is only used for input, you can use in. It's the convenience because the compiler cannot be sure if you can remember which form is called covariance and which is called contravariance. If you don't declare them explicitly once the type has been declared, the relevant types of conversion are available implicitly.
There is no variance (either covariance or contravariance) in classes because even if you have a class that only uses the type parameter for input (or only uses it for output), you can’t specify the in or out modifiers. Only interfaces and delegates can have variant type parameters. Firstly the CLR doesn’t allow it. From the conceptual point of view Interfaces represent a way of looking at an object from a particular perspective, whereas classes are more actual implementation types.
It means that if you have this:
class Parent { }
class Child : Parent { }
Then an instance of IFoo<Child>
is also an instance of IFoo<Parent>
.
精彩评论