With <out T> you can handle the interface link as one at the top of the hierarchy.
With <in T> you can handle the link to the interface as one from below in hiearchy.
Let me explain this in more English.
Let's say you get a list of animals from your zoo, and you intend to process them. All animals (in your zoo) have a name and a unique identifier. Some animals are mammals, some are reptiles, some are amphibians, some are fish, etc., but they are all animals.
So, with your list of animals (which contains animals of different types), you can say that all animals have a name, so it would obviously be safe to get the name of all animals.
However, if you only have a list of fish, but you need to treat them like animals, does it work? Intuitively, it should work, but in C # 3.0 and earlier this piece of code will not compile:
IEnumerable<Animal> animals = GetFishes(); // returns IEnumerable<Fish>
The reason for this is that the compiler does not “know” what you intend or can do with the collection of animals after you extract it. As far as you know, there may be a path through IEnumerable<T> to return the object to the list, and this will potentially allow you to put an animal that is not a fish in a collection that should only contain fish.
In other words, the compiler cannot guarantee that this is not allowed:
animals.Add(new Mammal("Zebra"));
Thus, the compiler simply categorically refuses to compile your code. This is covariance.
Let's look at contravariance.
Since our zoo can process all animals, it certainly can process fish, so try adding fish to our zoo.
In C # 3.0 and earlier, this does not compile:
List<Fish> fishes = GetAccessToFishes(); // for some reason, returns List<Animal> fishes.Add(new Fish("Guppy"));
Here, the compiler can resolve this part of the code, even if the method returns a List<Animal> simply because all the fish are animals, so if we just changed the types to this:
List<Animal> fishes = GetAccessToFishes(); fishes.Add(new Fish("Guppy"));
Then this will work, but the compiler cannot determine that you are not trying to do this:
List<Fish> fishes = GetAccessToFishes(); // for some reason, returns List<Animal> Fish firstFist = fishes[0];
Since the list is actually a list of animals, this is not permitted.
Thus, contradiction and codimension is how you process references to objects and what you are allowed to do with them.
The in and out keywords in C # 4.0 specifically mark an interface as one or the other. With in you can put a generic type (usually T) at the input position, which means method arguments and write-only properties.
With out you can put a generic type in the output positions, which are method return values, read-only properties, and out method parameters.
This will allow you to do what you intend to do with the code:
IEnumerable<Animal> animals = GetFishes(); // returns IEnumerable<Fish> // since we can only get animals *out* of the collection, every fish is an animal // so this is safe
List<T> has both inputs and outputs on T, so it is neither covariant nor contravariant, but an interface that allows you to add objects, for example:
interface IWriteOnlyList<in T> { void Add(T value); }
allow you to do this:
IWriteOnlyList<Fish> fishes = GetWriteAccessToAnimals(); // still returns IWriteOnlyList<Animal> fishes.Add(new Fish("Guppy")); <-- this is now safe
Here are a few videos that show concepts:
Here is an example:
namespace SO2719954 { class Base { } class Descendant : Base { } interface IBibbleOut<out T> { } interface IBibbleIn<in T> { } class Program { static void Main(string[] args) {
Without these labels, the following compilation:
public List<Descendant> GetDescendants() ... List<Base> bases = GetDescendants(); bases.Add(new Base()); <-- uh-oh, we try to add a Base to a Descendant
or that:
public List<Base> GetBases() ... List<Descendant> descendants = GetBases(); <-- uh-oh, we try to treat all Bases as Descendants