There are the following 4 objects declared:
abstract class AConfigAction {}
abstract class APlugin<ConfigActionType> where ConfigActionType :AConfigAction {}
class AppExecuteConfigAction : AConfigAction {}
class AppExecutePlugin : APlugin<AppExecuteConfigAction>{}
- All classes are public. Bodies have been removed for simplicity.
Why this fails to be converted?
_plugins = new List<APlugin<AConfigAction>>();
_plugins.Add(new AppExecutePlugin()); <--- Error
cannot convert from ‘AppExecutePlugin’ to ‘APlugin’
Full error message:
Error 1 The best overloaded method match for ‘System.Collections.Generic.List>.Add(EnvironmentSwitcher.Model.ConfigAction.APlugin)’ has some invalid arguments R:\projects\EnvironmentSwitcher\EnvironmentSwitcher\View\ConfigurationActionManagerForm.cs 35
Error 2 Argument ‘1’: cannot convert from ‘EnvironmentSwitcher.Model.ConfigAction.AppExecute.AppExecutePlugin’ to ‘EnvironmentSwitcher.Model.ConfigAction.APlugin’ R:\projects\EnvironmentSwitcher\EnvironmentSwitcher\View\ConfigurationActionManagerForm.cs 35
Let’s make that a bit easier to understand:
Suppose that were legal. What stops this?
firstCage is of type
Cage<Animal>which implies that it can hold any kind of animal. But in fact we know that it is a tigers-only cage. You just put a shark into a tiger cage, which seems uncomfortable for both the shark and the tiger.Obviously that cannot be allowed. What prevents it? The only thing that prevents it is that it is illegal to put a tiger cage into a collection of animal cages in the first place. A tiger cage is not a kind of animal cage because there are things you can do with an animal cage that you cannot do with a tiger cage, namely, put a shark into it. A basic principle of object-oriented design is that subtypes can do everything their supertypes can do; a tiger cage cannot do everything an animal cage can do, so it is not a subtype.
The more highfalutin way to say this is that generic types cannot be made to be covariant in their type arguments because doing so would violate the Liskov Substitution Principle. In C# 4, certain interfaces and delegates are covariant in their type arguments. For example, it is legal in C# 4 to put an
IEnumerable<Tiger>into aList<IEnumerable<Animal>>>because there’s no way that this can be made unsafe. We can uphold the substitution principle while allowing covariance becauseIEnumerable<T>is an “out-only” interface. You only ever take tigers out; there’s no way to put sharks in.