2

I was wondering , why does IEnumerable<T> has ONLY the out and not the in contravariant flag ?

public interface IEnumerable<out T>

I can understand the out by this example :

IEnumerable<string> strings = new List<string>(){"a","b","c"};
IEnumerable<object> objects = strings;

object is bigger than string , so the compiler afraid that well do something like:

 objects = objects.First().Select(x=>5);// ( we cant insert int to string...)

fine and understood.

but what about if i want to use the IEnumerable as insertion?

something like :

IEnumerable<object> objects = ...;
IEnumerable<string> strings = objects

so i can also INSERT into objects ...

but the problem is that there is no IEnumerable<in T> ...

am I missing something here?

Branko Dimitrijevic
  • 50,809
  • 10
  • 93
  • 167
Royi Namir
  • 144,742
  • 138
  • 468
  • 792
  • @downvoter , care to explain please ? – Royi Namir May 07 '12 at 08:41
  • 3
    what do you mean with "INSERT into objects", you can't insert into `IEnumerable` and I can't see an insertion anywhere in your question? What is your real problem? – Tomas Jansson May 07 '12 at 08:49
  • @TomasJansson I want to be able to insert strings values into objects. but Ienumerable doesnt has `In ` it is only out – Royi Namir May 07 '12 at 08:54
  • Almost like [why-was-ienumerablet-made-covariant-in-c-sharp-4](http://stackoverflow.com/questions/6732299/why-was-ienumerablet-made-covariant-in-c-sharp-4). – nawfal Jun 10 '13 at 16:16

6 Answers6

4

You can't insert into IEnumerable. There is, unfortunately, no "IInsertable".

There is no interface to tell that you can only add items to a collection. This would be an in generic type. Because you can get the items on every collection interface, it is not possible to have in generic arguments.

Edit

The interface IEnumerable<T> has only getters, so you can't insert any elements to the collection using that interface. Because of that, out is allowed in the declaration of IEnumerable<out T>. It tells the compiler, that values of type T are only going in one direction, the are "coming out". This makes it compatible in one direction. You can use IEnumerable<string>as an IEnumerable<object>, because you are only reading from it. You can read a string as an object. But you couldn't write to it, you couldn't pass an object as a string. It might be an integer or any other type.

Stefan Steinegger
  • 63,782
  • 15
  • 129
  • 193
  • i didnt tent to insert into Ienumerable but VIA Iernumerable – Royi Namir May 07 '12 at 08:47
  • @Royi: My question is: *how* would you insert *via* IEnumerable? – Stefan Steinegger May 07 '12 at 08:57
  • so what you telling me is that i cant udpate Ienumerable types ? what about `strings =strings .Select(x=>"myInsertedString")` ? – Royi Namir May 07 '12 at 09:01
  • @RoyiNamir that is not inserting, that is replacing the whole list. – Bas May 07 '12 at 09:02
  • @BasBrekelmans this is changing the current list ... i didnt make any *new obj* here... – Royi Namir May 07 '12 at 09:03
  • @Royi: To be very precise: You can't update a type using `IEnumerable`. Of course you can update for instance a `List`, but not using the `IEnumerable` interface. – Stefan Steinegger May 07 '12 at 10:33
  • @RoyiNamir, you don't have to use `new obj` for it to be a new object. What you are doing in you example is not changing the list, you are just applying a "filter" to your current list and then assigning the result to your existing variable. The result from your filtering is new, the variable is just a pointer to any object of that type. – Tomas Jansson May 07 '12 at 11:31
  • @TomasJansson read this http://stackoverflow.com/questions/3930510/simple-way-to-update-ienumerable-objects-using-linq with jon skeet answer.... he is actualy says that you ARE CHANGING the values and not creating new ones ! – Royi Namir May 07 '12 at 12:14
  • @Royi: No, in that case, it is changing the elements in the collection, not the collection itself. The collection implements IEnumerable, and it does not change. – Stefan Steinegger May 07 '12 at 12:20
  • @StefanSteinegger so the out in `IEnumerable` tells me that i cant insert via it ???? or as a general rule - the Ienumerable can be used to insert...? or is it just because `public interface IEnumerator : IDisposable, IEnumerator { // Properties T Current { get; } } ` has only get accessor....? – Royi Namir May 07 '12 at 12:23
  • @RoyiNamir I think the word you are looking for is "assign" (not "insert"). In your example: `IEnumerable strings = objects` is assignment not insertion (neither collection nor its elements are changed in any way - you just treat it slightly differently from the perspective of the type system). – Branko Dimitrijevic May 07 '12 at 12:42
  • @RoyiNamir: Yes, you are changing the values but not the collection itself. I've extended my answer to include a little information about covariance and contravariance. – Tomas Jansson May 07 '12 at 12:48
  • It's too much for a comment, so I add a section to the answer. – Stefan Steinegger May 07 '12 at 13:43
4

Eric Lippert has a series of posts on this prior to introducing co/contravariance in C# which is worth a read.

a simple example would be

public class Animal(){
...
}

public class Giraf(){
   public void LookAboveTheClouds(){}
}

IEnumerable<Animal> animals = new list<Animal>{
                                    new Elephant(),
                                    new Tiger(), 
                                    new TinyMouse()};
IEnumerable<Giraf> girafs = animals;

foreach(var giraf in girafs){
   //elephants,tigers and tiny mice does not suuport this
   giraf.LookAboveTheClouds() 
}

in other words there's no way the compiler can guarantee that you can safely use your IEnumerable<Animal> as an IEnumerable<Giraf>. All girafs are animals but not all animals are girafs

Rune FS
  • 21,497
  • 7
  • 62
  • 96
2

I don't see your problem here. When using IEnumerable you can't insert, and that is not what you are doing. What you are trying to do is assigning a IEnumerable<string> a value of IEnumerable<object> and that won't work. When you are assigning your IEnumerable<string> with an object that is of type IEnumerble<object> you can't guarantee that all the objects in the list is of type string. That is why can't do that.

Covariant, the out keyword, interfaces ensures that the output can always be understood by the consuming part, that is, an IEnumerable<string> can be casted to a list of IEnumerable<object> since string : object and someone consuming a list of object must know how to handle a string but not the other way around.

Contravariant, the in keyword, interfaces enables you to cast objects to less specified generics, that is, IComparer<object> can be casted to IComparer<string> since a class implementing IComparer<object> must now how to handle string since it is contravariant but not the other way around.

Read more here: http://msdn.microsoft.com/en-us/library/dd799517.aspx

Tomas Jansson
  • 22,767
  • 13
  • 83
  • 137
1

I think it's strictly realted to type safity: what if you in your list
IEnumerable<object> {"Hello", 2.34d, new MyCoolObject(), enumvalue} ?

How runtime should treat those conversions ?

In case of string it could be a simple case, cause it should be enough to call override (if any) of ToString() , but framework designer can not rely on custom cases and should provide generic solutions.

Hope this helps.

Tigran
  • 61,654
  • 8
  • 86
  • 123
  • Hi , object can contain ANYTHING , so if i write something like `IEnumerable strings = objects` i want to do something like (psuedo) `strings[0]="lala"` so where is the problem, what do i care whats in Objects ??? I only want to insert Strings ! .....:) – Royi Namir May 07 '12 at 08:50
  • @RoyiNamir: I wrote in my answer, that for string it *may be* even could be done, but framewrok designer can not add a feature only for *one, specific case*. What if instead of IEnumerable you want to convert to IEnumerable ? Hope I'm explained. – Tigran May 07 '12 at 08:52
1

As already mentioned in the comments, you cannot use IEnumerable to modify a collection.

The compiler will not allow code like the one below due to type safety issues as the one you mentioned (inserting int to an list of string) because you can modify the strings lists here using objects

IEnumerable is simply an iterator over a collection.

static void Main(string[] args)
{
    IList<string> strings = new List<string> { "a", "b", "c" };
    IList<object> objects = strings;  ** // This will give an error because compiler does not want you to do the line below **
    objects.Add(5);  // Type safety violated for IList<string> hence not allowed


    // The code below works fine as you cannot modify the strings collection using IEnumerable of object
    //IEnumerable<string> strings = new List<string> { "a", "b", "c" };
    //IEnumerable<object> objects = strings;
}

Hope this helps.

Dinesh
  • 3,652
  • 2
  • 25
  • 35
0

You can't assign IEnumerable<Parent> to IEnumerable<Child> because it may contain elements that are not Child.

but what about if i want to use the IEnumerable as insertion? something like :

IEnumerable<object> objects = ...;
IEnumerable<string> strings = objects

There is no way to know at compile time that the IEnumerable<object> doesn't contain a non-string. However, if you (the programmer) know there are only strings there, you can do the casting at run-time:

IEnumerable<string> strings = objects.Cast<string>();

Incidentally, you might want to take a look at description of the tag .

Community
  • 1
  • 1
Branko Dimitrijevic
  • 50,809
  • 10
  • 93
  • 167