Given the following LinqPad example:
void Main()
{
List<DbItem> dbItems = new List<DbItem>
{
new DbItem{Id = 4, SomeValue = "Value For 4", OtherValue = 1},
new DbItem{Id = 19, SomeValue = "Value For 19", OtherValue = 2}
};
ListMethod(dbItems.Cast<IDbItem>().ToList()); <-+
EnumerableMethod(dbItems); <-+
} |
|
// These are the methods are I asking about -------+
public void ListMethod(List<IDbItem> dbItems)
{
Console.WriteLine(dbItems);
}
public void EnumerableMethod(IEnumerable<IDbItem> dbItems)
{
Console.WriteLine(dbItems);
}
public class DbItem : IDbItem
{
public int Id {get; set;}
public string SomeValue {get; set;}
public int OtherValue {get; set;}
}
public interface IDbItem
{
int Id {get; set;}
string SomeValue {get; set;}
}
Why can EnumerableMethod
take the list directly while ListMethod
needs a cast first?
I get that an cast is happening from DbItem to IDbItem, but what is different about the IEnumerable that allows it to make the cast without an express request?
I imagine the answer involves the word Covariance, but I don't know enough about that to just figure it out myself.
An IEnumerable<T> that contains each element of the source sequence cast to the specified type. source is null. An element in the sequence cannot be cast to type TResult. The following code example demonstrates how to use Cast<TResult> (IEnumerable) to enable the use of the standard query operators on an ArrayList.
An IEnumerable<T> that contains each element of the source sequence cast to the specified type. source is null. An element in the sequence cannot be cast to type TResult.
For example, ArrayList does not implement IEnumerable<T>, but by calling Cast<TResult> (IEnumerable) on the ArrayList object, the standard query operators can then be used to query the sequence. If an element cannot be converted to type TResult, this method throws a InvalidCastException.
If you use a wider interface type such as IList, you are more in danger of breaking code changes. If someone wants to call your method with a custom defined object which only implements IEnumerable, it simply won’t work and will result in a compilation error.
Check out this post about covariance. He gives an explanation and a way to make the code work without the cast. Lists are not covariant, but you can just change the method like so:
public void ListMethod<T>(List<T> dbItems) where T : IDbItem
{
Console.WriteLine(dbItems);
}
Covariance is the ability to assign a more derived generic type to a base generic type. The reason List
is not covariant is because you could do this:
class Animal {}
class Dog : Animal {}
class Cat : Animal {}
void someFunction()
{
List<Animal> dogs = new List<Dog>();
dogs.Add(new Cat());
}
dogs
is really a list of dogs, not of animals. Therefore adding a Cat
to it would be a problem, specifically causing a compiler error.
I imagine the answer involves the word Covariance
Yes you are correct, The type parameter for IEnumerable<T>
is covariant. so you can use either the type you specified or any type that is more derived
public interface IEnumerable<out T> : IEnumerable
Where as List<T>
type parameter is not covariant
public class List<T> : IList<T>, ICollection<T>,
IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>,
IEnumerable
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With