I have a function that has a signature of
Update(IEnumberable<INotifyPropertyChanging> things)
and a class
doohickey : INotifyPropertyChanging, INotifyPropertyChanging{}
When I try to do
List<doohickey> doohickeys = new List<doohickey>();
Update(doohickeys);
An exception gets thrown stating:
cannot convert from 'System.Collections.Generic.List<doohickey>' to 'System.Collections.Generic.IEnumerable<System.ComponentModel.INotifyPropertyChanging>'
What gives? Doohickey inherits the INotifyPropertyChanging interface and List inherits the IEnumerable interface! How come simply passing the object and expecting it to get cast down doesn’t?
I’ve included the references to INotifyPropertyChanging to signify that doohickey is actually a linq-to-sql class; in case that bit of context matters.
This won’t work because there is no implicit conversion between
List<doohickey>andIEnumerable<NotifyPropertyChanging>. You need to call:The reason behind this is type safety. In C#4, co-/contravariance was added to the language, which generally helps making these type of conversions valid. There are some limitations though, since the type involved needs to be declared co-/contravariant, and it only applies to interfaces and delegates.
The reason why you can’t implicitly convert
List<T>toList<TBase>is, that it would make this code valid:Now, if you tried to do this when
valueswas of typeIEnumerable<T>, it would be valid in C#4. This is because you can only get values out ofIEnumerable<T>, so we don’t need to worry about lesser types being added. ThereforeIEnumerable<T>is covariant, and is declared as:Where
outmeans “covariant”. (The keywords is actually the best way to remember which way values may go in a variant type.Co-/contravariance is a rather large subject, but this article does a good job of explaining the most important parts. And if you want to learn more, Eric Lippert has an 11-part blog post series about the feature. Eric is one of the language designers.