<out T> vs <T> in Generics
The out
keyword in generics is used to denote that the type T in the interface is covariant. See Covariance and contravariance for details.
The classic example is IEnumerable<out T>
. Since IEnumerable<out T>
is covariant, you're allowed to do the following:
IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings;
The second line above would fail if this wasn't covariant, even though logically it should work, since string derives from object. Before variance in generic interfaces was added to C# and VB.NET (in .NET 4 with VS 2010), this was a compile time error.
After .NET 4, IEnumerable<T>
was marked covariant, and became IEnumerable<out T>
. Since IEnumerable<out T>
only uses the elements within it, and never adds/changes them, it's safe for it to treat an enumerable collection of strings as an enumerable collection of objects, which means it's covariant.
This wouldn't work with a type like IList<T>
, since IList<T>
has an Add
method. Suppose this would be allowed:
IList<string> strings = new List<string>();
IList<object> objects = strings; // NOTE: Fails at compile time
You could then call:
objects.Add(new Image()); // This should work, since IList<object> should let us add **any** object
This would, of course, fail - so IList<T>
can't be marked covariant.
There is also, btw, an option for in
- which is used by things like comparison interfaces. IComparer<in T>
, for example, works the opposite way. You can use a concrete IComparer<Foo>
directly as an IComparer<Bar>
if Bar
is a subclass of Foo
, because the IComparer<in T>
interface is contravariant.
consider,
class Fruit {}
class Banana : Fruit {}
interface ICovariantSkinned<out T> {}
interface ISkinned<T> {}
and the functions,
void Peel(ISkinned<Fruit> skinned) { }
void Peel(ICovariantSkinned<Fruit> skinned) { }
The function that accepts ICovariantSkinned<Fruit>
will be able to accept ICovariantSkinned<Fruit>
or ICovariantSkinned<Banana>
because ICovariantSkinned<T>
is a covariant interface and Banana
is a type of Fruit
,
the function that accepts ISkinned<Fruit>
will only be able to accept ISkinned<Fruit>
.
For remembering easily the usage of in
and out
keyword (also covariance and contravariance), we can image inheritance as wrapping:
String : Object
Bar : Foo