Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does IEnumerable not need a cast but a List does?

Tags:

c#

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.

like image 948
Vaccano Avatar asked Sep 05 '13 23:09

Vaccano


People also ask

What is cast<TResult> (IEnumerable) in Java?

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.

What is an IEnumerable<T>?

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.

Does ArrayList implement IEnumerable in Java?

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.

Should I use IEnumerable or IList for my interface?

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.


2 Answers

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.

like image 189
ShadowCat7 Avatar answered Oct 30 '22 19:10

ShadowCat7


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
like image 31
sa_ddam213 Avatar answered Oct 30 '22 19:10

sa_ddam213