Home c# What's the difference in arrays IEnumerable & lt; T & gt; and...

What’s the difference in arrays IEnumerable & lt; T & gt; and List & lt; T & gt ;?

Author

Date

Category

Perhaps the question is not entirely correct.
Let’s say we have the following code:

List & lt; int & gt; listValues ​​= new List & lt; int & gt; {1, 2, 3};
      var t1 = listValues.ToList ();

In this case, the t1 variable will refer to a new List object with the same values.

IEnumerable & lt; int & gt; listValues2 = new List & lt; int & gt; {1, 2, 3};
    var t2 = listValues2.ToList ();

Here, as I understand it, what is happening is that t2 refer to a new IEnumerable object with similar values ​​(the t2 array contains 1,2,3).

The difference is that when adding elements in the second case, you must explicitly cast to the List type, right?

((List & lt; int & gt;) listValues2) .Add (4);

And / or are there other differences in this case?


Answer 1, authority 100%

t2 , like t1 , will refer to the new List & lt; int & gt; object. IEnumerable & lt; T & gt; is an interface, you cannot instantiate an interface.

listValues2 is an interface link. It can point to any instance of any class that implements IEnumerable & lt; int & gt; . For example, an instance of the class Queue & lt; int & gt; or Stack & lt; int & gt; .

IEnumerable & lt; T & gt; is required for the foreach statement because it defines the GetEnumerator () method. This interface does not define the Add () method. This method is defined in the ICollection & lt; T & gt; interface, which is also implemented by the List & lt; T & gt; class. That is why, to add elements by reference listValues2 , you have to explicitly cast the types first. However, if the link listValues2 pointed to an instance of a class other than List & lt; int & gt; , a runtime exception will occur.


Answer 2, authority 100%

Here, as I understand it, what is happening is that t2 refer to a new IEnumerable object with similar values ​​

The runtime type is the same in both cases – List. Moreover, even if you replace var ... with IEnumerable & lt; int & gt; ... , nothing will change, the runtime type will remain the same, since the ToList method always returns an object of type List. (In addition, the object simply cannot be of type IEnumerable, since the interface cannot be instantiated.)

To see it in practice, run the code:

List & lt; int & gt; listValues ​​= new List & lt; int & gt; {1, 2, 3};
var t1 = listValues.ToList ();
Console.WriteLine (t1.GetType ());
IEnumerable & lt; int & gt; listValues2 = new List & lt; int & gt; {1, 2, 3};
var t2 = listValues2.ToList ();
Console.WriteLine (t2.GetType ());
IEnumerable & lt; int & gt; listValues3 = new List & lt; int & gt; {1, 2, 3};
IEnumerable & lt; int & gt; t3 = listValues3.ToList ();
Console.WriteLine (t3.GetType ());

It will output

System.Collections.Generic.List`1 [System.Int32]
System.Collections.Generic.List`1 [System.Int32]
System.Collections.Generic.List`1 [System.Int32]

To still get a runtime type other than List, you can implement an iterator method:

static IEnumerable & lt; int & gt; Foo (List & lt; int & gt; list)
{
  for (int i = 0; i & lt; list.Count; i ++) yield return list [i];
}

Then

var t4 = Foo (listValues);
Console.WriteLine (t4.GetType ());

Will output the name of the automatically generated compiler-type iterator (I have ConsoleApplication1.Program + & lt; Foo & gt; d__0 )


Answer 3, authority 100%

Let me try to explain it a little in my own way.

I’ll come from the other end: why in practice you might need interfaces?

Let’s say you wanted to output the contents of the List & lt; int & gt; to the console and you wrote a method like this:

static void Print (List & lt; int & gt; list)
{
  foreach (var n in list)
    Console.Write (n + "");
  Console.WriteLine ();
}

Now you can use it:

List & lt; int & gt; list = new List & lt; int & gt; {1, 2, 3};
Print (list);

Convenient.

But what if we wanted to output an array? We try to use the same method:

int [] array = new int [] {4, 5, 6};
Print (array);

Alas, this is impossible.

How to be? Let’s see what exactly is done with the list in this method: it is enumerated (enumerate). Nothing is added to it, nothing is changed, nothing is deleted. All that is required of it is to allow itself to be listed (traversed).

The IEnumerable interface just means that the type that implements it is enumerable – you can loop over it.

Let’s change our method as follows:

static void Print (IEnumerable & lt; int & gt; list)
{
  foreach (var n in list)
    Console.Write (n + "");
  Console.WriteLine ();
}

The code now Print (array); compiles and works successfully.

Now we can pass to this method any type that implements the IEnumerable & lt; int & gt; interface. Of course, we’re limited to int here, but we’ll leave that out of the scope of this discussion.

Moreover, another programmer using our library with this method, by the signature of this method alone, will say what can happen to the collection that he will input: it will be listed and nothing more. And if the method accepts List & lt; int & gt; , then inside the method, adding new data to the list or deleting them, or other unwanted actions may well occur …

Thus, interfaces allow using not one hard-coded type, but many different ones. And also the interfaces make it clear what actions are allowed.

PS: IReadOnly * interfaces provide even more guarantees for data immutability.


Answer 4

The answer was a little late, but still. It is important to understand that the type of a variable and the type of its value are not the same thing. For example:

using System;
using System.Collections.Generic;
namespace CSrharpApplicationTest
{
  internal class Program
  {
    private static void Main (string [] args)
    {
      IEnumerable & lt; int & gt; x = new List & lt; int & gt; ();
      Console.WriteLine (x.GetType ());
    }
  }
}

The type of the variable is IEnumerable & lt; int & gt; , the type of its value is List & lt; int & gt; .

In both cases, in your examples, the t1 and t2 types are List & lt; int & gt; , thanks to the call to the ToList ( ) , the implementation of which looks like this:

[__DynamicallyInvokable]
    public static List & lt; TSource & gt; ToList & lt; TSource & gt; (this IEnumerable & lt; TSource & gt; source)
    {
      if (source == null)
      {
        throw Error.ArgumentNull ("source");
      }
      return new List & lt; TSource & gt; (source);
    }

where the constructor List & lt; TSource & gt; is described as follows:

[__DynamicallyInvokable]
    public List (IEnumerable & lt; T & gt; collection)
    {
      if (collection == null)
      {
        ThrowHelper.ThrowArgumentNullException (ExceptionArgument.collection);
      } 
ICollection & lt; T & gt; collection2 = collection as ICollection & lt; T & gt ;;
      if (collection2! = null)
      {
        int count = collection2.Count;
        if (count == 0)
        {
          _items = _emptyArray;
        }
        else
        {
          _items = new T [count];
          collection2.CopyTo (_items, 0);
          _size = count;
        }
      }
      else
      {
        _size = 0;
        _items = _emptyArray;
        foreach (T item in collection)
        {
          Add (item);
        }
      }
    }

The difference is that when adding elements in the second case, you must explicitly cast to the List type, right?

Yes, in the second case, you tell the compiler that the type listValues2 is any type that implements IEnumerable & lt; int & gt; . In the interface IEnumerable & lt; out T & gt; , there are no methods for adding / changing / removing elements, because it describes the minimum functionality for the ability to enumerate over a collection, so the compiler cannot guarantee that what’s inside listValues2 is a collection, with the Add method. Therefore, an explicit cast to List & lt; int & gt; is required.

Programmers, Start Your Engines!

Why spend time searching for the correct question and then entering your answer when you can find it in a second? That's what CompuTicket is all about! Here you'll find thousands of questions and answers from hundreds of computer languages.

Recent questions