(I think this question is more about covariance rather than contravariance, since the example given is about covariance.)
List<Fish> fishes = GetAccessToFishes(); // for some reason, returns List<Animal>
Out of context, this is a bit misleading. In your example, the author intended to convey the idea that, technically, if List<Fish> actually referred to List<Animal> , it would be safe to add Fish to it.
But, of course, this will also add Cow to it, which is clearly wrong.
Therefore, the compiler does not allow you to assign a List<Animal> link to a List<Fish> link.
So, when will it be really safe and helpful?
This is a safe destination if the fee cannot be changed. In C #, an IEnumerable<T> can be an unmodifiable collection.
So you can do it safely:
IEnumerable<Animal> animals = GetAccessToFishes(); // for some reason, returns List<Animal>
because there is no way to add non-fish to animals . It has no methods to allow you to do this.
So, when will it be useful?
This is useful when you want to access a common method or property of a collection that can contain elements of one or more types derived from the base class.
For example, you might have a hierarchy representing different categories of stocks for a supermarket.
Let's say that the base class StockItem has a double SalePrice property.
Suppose also that you have a Shopper.Basket() method that returns an IEnumerable<StockItem> that represents the elements that the customer has in his cart. Items in the cart can be of any specific type derived from StockItem .
In this case, you can add prices for all items in the basket (I wrote this entry without using Linq to clearly define what is happening. The real code will use IEnumerable.Sum() , of course):
IEnumerable<StockItem> itemsInBasket = shopper.Basket; double totalCost = 0.0; foreach (var item in itemsInBasket) totalCost += item.SalePrice;
contravariance
An example of using contravariance is when you want to apply some action to an element or collection of elements using the base class type, even if you have a derived type.
For example, you might have a method that applied an action to each element in a StockItem sequence as follows:
void ApplyToStockItems(IEnumerable<StockItem> items, Action<StockItem> action) { foreach (var item in items) action(item); }
Using the StockItem example, suppose it has a Print() method that you can use to print it before receiving confirmation. You can call using it as follows:
Action<StockItem> printItem = item => { item.Print(); } ApplyToStockItems(shopper.Basket, printItem);
In this example, the types of items in the basket can be Fruit , Electronics , Clothing and so on. But since they all come from StockItem , the code works with all of them.
I hope the usefulness of this code is clear! This is very similar to how many methods in Linq work.