263

What is the difference between <out T> and <T>? For example:

public interface IExample<out T>
{
    ...
}

vs.

public interface IExample<T>
{
    ...
}
Cole Tobin
  • 9,206
  • 15
  • 49
  • 74
  • 2
    Good example would be IObservable and IObserver, defined in system ns in mscorlib. public interface IObservable, and public interface IObserver. Similarly, IEnumerator, IEnumerable – VivekDev Feb 06 '16 at 01:00
  • 5
    The best explanation I met: https://agirlamonggeeks.com/2019/05/29/vs-in-generic-interfaces-contravariance-vs-covariance-the-easier-part-1/ .(< in T> <– means that T can be only passed as a parameter to a method; <– means that T can be only returned as method results ) – Uladzimir Sharyi Aug 16 '19 at 08:55

7 Answers7

273

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.

janw
  • 8,758
  • 11
  • 40
  • 62
Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • 5
    @ColeJohnson Because `Image` is an abstract class ;) You can do `new List() { Image.FromFile("test.jpg") };` with no problems, or you can do `new List() { new Bitmap("test.jpg") };` as well. The problem with yours is that `new Image()` isn't allowed (you can't do `var img = new Image();` either) – Reed Copsey Aug 20 '12 at 16:28
  • 4
    a generic `IList` is a bizarre example, if you want `object`s you don't need generics. – Jodrell Dec 18 '13 at 14:54
  • 7
    @ReedCopsey Aren't you contradicting your own answer in your comment? – MarioDS Sep 21 '15 at 10:05
81

For remembering easily the usage of in and out keyword (also covariance and contravariance), we can image inheritance as wrapping:

String : Object
Bar : Foo

in/out

shA.t
  • 16,580
  • 5
  • 54
  • 111
o0omycomputero0o
  • 3,316
  • 4
  • 31
  • 45
  • 18
    Isn't this the wrong way around? Contravariance = in = allows less derived types to be used in place of more derived. / Covariance = out = allows more derived types to be used in place of less derived. Personally, looking at your diagram, I read it as the opposite of the that. – Sam Shiles Aug 24 '17 at 07:19
  • co **u** variant (: for me – Soner from The Ottoman Empire Mar 20 '20 at 11:25
  • Can we use them at the same time? – Luka Samkharadze Jul 26 '22 at 20:26
  • @SamShiles and technically, a derived type (`string`) should be "bigger" than its base type (`object`) because it _should_ have more members. So the image works if you just swap the types. – Luke Vo Jan 31 '23 at 04:48
65

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>.

Jodrell
  • 34,946
  • 5
  • 87
  • 124
58

"out T" means that type T is "covariant". That restricts T to appear only as a returned (outbound) value in methods of the generic class, interface or method. The implication is that you can cast the type/interface/method to an equivalent with a super-type of T.
E.g. ICovariant<out Dog> can be cast to ICovariant<Animal>.

shA.t
  • 16,580
  • 5
  • 54
  • 111
James World
  • 29,019
  • 9
  • 86
  • 120
  • 29
    I didn't realize that `out` enforces that `T` can be returned only, until I read this answer. The whole concept makes more sense now! – MarioDS Sep 21 '15 at 10:09
8

From the link you posted....

For generic type parameters, the out keyword specifies that the type parameter is covariant.

EDIT: Again, from the link you posted

For more information, see Covariance and Contravariance (C# and Visual Basic). http://msdn.microsoft.com/en-us/library/ee207183.aspx

Brad Cunningham
  • 6,402
  • 1
  • 32
  • 39
2

I think this screenshot from VS2022 is pretty descriptive - it says what sort of constraints this put on generics:

generics variance constraing

lissajous
  • 371
  • 5
  • 17
2

The simplest explanation I found is in this article stated as

interface ISomeName<in T> <– means that T can be only passed as a parameter to a method (it enters inteface’s methods, so it goes inside, maybe that’s why we use here keyword ‘in’… Hmm, no, that’s just a coincidence ?)

interface ISomeName<out T> <– means that T can be only returned as method results (it is what we receive from a method so it goes out of it – wow, again sounds legit!)

Manish Rawat
  • 1,142
  • 1
  • 18
  • 34