9

I have two questions:

Question 1 Background : I noticed when looking at the implementation of 'AsEnumerable()' method in LINQ from Microsoft, which was:

public static IEnumerable<TSource> AsEnumerable<TSource>(this IEnumerable<TSource> source)
{ 
   return source;
} 

Question 1: I was expecting some kind of casting or something here , but it simply returns the value it was passed. How does this work ?

Question 2/3 Background : I have been trying to understand Covariance , contravariance and Invariant. I think, I have a vague understanding that 'in' and 'out' keywords determine the polymorphic behavior when assigning a subtype to a parent type.

Question 2: I know from reading that IEnumerable is covariant, and List is invariant then why is this not possible :

List<char> content = "testString".AsEnumerable();

Question 3:
If IList implements IEnumerable then why is this not possible :

IEnumerable<char> content1 = "testString";
IList<char> content2 = content1;

Please help me understanding, thank you in advance.

RaM
  • 1,126
  • 10
  • 25
  • AsEnumerable() forces the query to execute immediately in other LINQ providers like LINQ to SQL or Entity Framework. It's probably added to LINQ to Objects for completeness. – Ufuk Hacıoğulları Jul 31 '13 at 14:13

1 Answers1

3
  1. The input argument is already known to have the type IEnumerable<TSource>. Why would it need to cast anything? Casting the objects to the type TSource would have no effect, since they're already guaranteed to be of that type (or a more derived type).

  2. You can't assign a value of type IEnumerable<char> to a variable of type List<char>. I think you're thinking in reverse here; List<char> derives from IEnumerable<char>, not the other way around. This has nothing to do with List<T> being invariant. IEnumerable<T> is covariant (to be more precise, the type parameter T is covariant), which gives us this situation:

    IEnumerable enumerable = Enumerable.Empty<string>(); // Allowed
    IEnumerable<string> genericEnumerable = enumerable; // Not allowed
    
  3. Again, IList<char> inherits from IEnumerable<char>, not the other way around. You can do this:

    IList<char> content1 = "testString".ToList();
    IEnumerable<char> content2 = content1;
    

    What you're asking for doesn't make sense, I'm afraid, and it's nothing to do with covariance. The fact that IEnumerable<T> is covariant means that you're allowed to do this:

    IEnumerable<object> asObject = new List<string>() { "test" };
    

    But List<T> is invariant, so you can't do this:

    List<object> asObject = new List<string>() { "test" };
    
Magnus Grindal Bakken
  • 2,083
  • 1
  • 16
  • 22
  • I need some time to digest that ! – RaM Jul 31 '13 at 14:22
  • In response to 1st answer, I understand, so If I can directly assigned a value of derived object to an instance of parent type then what purpose does 'AsEnumerable()' solve ? – RaM Jul 31 '13 at 14:27
  • In response to 2nd answer, Ok so Why is IEnumerable enumerable = (IEnumerable)Enumerable.Empty(); not possible ? IEnumerable implements IEnumerable so why is the compiler complaining ? – RaM Jul 31 '13 at 14:50
  • 1
    @RaM You're thinking in reverse again. `IEnumerable` implements `IEnumerable`, which means that you can assign an instance of `IEnumerable` to a variable of type `IEnumerable`, not the other way around. In the example you quoted, the compiler doesn't know that the variable actually *is* an instance of `IEnumerable`. I've edited the example to hopefully be a little clearer. – Magnus Grindal Bakken Jul 31 '13 at 14:56
  • As for AsEnumerable(), I'm not too sure what the point of it is either, to be honest. The remarks section of the [MSDN article](http://msdn.microsoft.com/en-us/library/bb335435.aspx) indicates that it could be used to "hide" special implementations of other LINQ methods like `Where` and `Select`, but that seems like a strange rationale to me. Maybe I've just never needed it. – Magnus Grindal Bakken Jul 31 '13 at 14:56
  • @MagnusGrindalBakken `AsEnumerable` is very useful in LINQ to SQL/EF as it splits the server side (translation) and client side processing of the LINQ statement. – NetMage Oct 11 '19 at 17:26