Normally when we declare an interface or a class, we would do something like this:
interface IFoo <T> {
T GiveMeSomething()
void TakeSomething(T instance)
}
This can provide us with restrictions though. Say we have the following class declarations:
class Fruit { }
class Apple : Fruit {}
class Banana : Fruit {}
One would expect that you could do something like this:
List<Fruit> fruitBowl = new List<Fruit>();
fruitBowl.Add(new Apple());
Obviously, we can not without casting it down to it's base type. The reason for this, is that we can read and write to the list. I could request an item for the list, but I don't really know what type it would be as I could add any type of fruit. This goes against the whole point of a strongly typed List. So naturally, we get a compile error.
Covariance is all to do with values coming out of an interface/class. Effectively making it read only.
interface IFoo <out T> {
T GiveMeSomething()
}
Covariance is effectively saying you can not modify this object, only take from it, hence the out parameter. The famous example being IEnumerbale<T>.
Contravariance is when values only go into a class/interface. Effectively making it write only.
interface IFoo <in T> {
void TakeSomething(T instance)
}
This is nice as it allows us to pass in any type of fruit, without having to cast it down to the base class. So I can do the following:
IFoo<Fruit> fruitbowl = new Foo<Fruit>();
fruitBowl.TakeSomething(new Apple());
fruitBowl.TakeSomething(new Banana());
fruitBowl.TakeSomething(new Apple());
What is the benefit of using these parameter specifications on the interface - out and in?
ReplyDelete