Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IEnumerable<T> vs. Array

I'm trying to get the idea, what would be the best way to publish a Readonly List of objects as a public method? From Eric Lippert's Blog, Arrays are kinda bad, because someone could easily add a new Entry. So one would have to pass a new Array every time the method is called. He suggests, to pass IEnumerable<T>, since this is per definition read only (no add, remove methods), which I practiced for quite sometime. But in our new project, people even started to create Arrays of these IEnumerables, because they don't know the DataSource behind, so they get a : Handling warning for possible multiple enumeration of IEnumerable

I'm interested in a technical approach, how one would solve this puzzle. The only solution I came up so far would be to use a IReadOnlyCollection, but this would be way more explicit than an IEnumerable.

What is best practice to publish such lists, which shouldn't be changed, but should be declared as In-Memory Lists?

like image 555
Matthias Müller Avatar asked Nov 22 '15 20:11

Matthias Müller


People also ask

Is IEnumerable an array?

IEnumerable is a behavior while Array is a data structure(Contiguous collection of elements with fixed size, facilitating accessing elements by indexes) .

What's the difference between IEnumerable T and List t?

IEnumerable is a deferred execution while List is an immediate execution. IEnumerable will not execute the query until you enumerate over the data, whereas List will execute the query as soon as it's called. Deferred execution makes IEnumerable faster because it only gets the data when needed.

Is IEnumerable faster than List?

So it isn't that IEnumerable<T> is more efficient than list in a "performance" or "runtime" aspect. It's that IEnumerable<T> is a more efficient design construct because it's a more specific indication of what your design requires. (Though this can lead to runtime gains in specific cases.)

When should I use IEnumerable?

IEnumerable interface is used when we want to iterate among our classes using a foreach loop. The IEnumerable interface has one method, GetEnumerator, that returns an IEnumerator interface that helps us to iterate among the class using the foreach loop.


2 Answers

Usually - and since a while - this solved using immutable collections.

Your public properties should be, for example, of type IImmutableList<T>, IImmutableHashSet<T> and so on.

Any IEnumerable<T> can be converted to an immutable collection:

  • someEnumerable.ToImmutableList();
  • someEnumerable.ToImmutableHashSet();
  • ... and so on.

This way you can work with private properties using mutable collections and provide a public surface of immutable collections only.

For example:

public class A {      private List<string> StringListInternal { get; set; } = new List<string>();      public IImmutableList<string> StringList => StringListInternal.ToImmutableList(); } 

There's also an alternate approach using interfaces:

public interface IReadOnlyA {      IImmutableList<string> StringList { get; } }  public class A : IReadOnlyA {      public List<string> StringList { get; set; } = new List<string>();      IImmutableList<string> IReadOnlyA.StringList => StringList.ToImmutableList(); } 

Check that IReadOnlyA has been explicitly-implemented, thus both mutable and immutable StringList properties can co-exist as part of the same class.

When you want to expose an immutable A, then you return your A objects upcasted to IReadOnlyA and upper layers won't be able to mutate the whole StringList in the sample above:

public IReadOnlyA DoStuff() {      return new A(); }  IReadOnlyA a = DoStuff();  // OK! IReadOnly.StringList is IImmutableList<string> IImmutableList<string> stringList = a.StringList; 

Avoiding converting the mutable list to immutable list every time

It should be a possible solution to avoid converting the source list into immutable list each time immutable one is accessed.

Equatable members

If type of items overrides Object.Equals and GetHashCode, and optionally implements IEquatable<T>, then both public immutable list property access may look as follows:

public class A : IReadOnlyA {      private IImmutableList<string> _immutableStringList;       public List<string> StringList { get; set; } = new List<string>();       IImmutableList<string> IReadOnlyA.StringList      {          get          {              // An intersection will verify that the entire immutable list              // contains the exact same elements and count of mutable list              if(_immutableStringList.Intersect(StringList).Count == StringList.Count)                 return _immutableStringList;              else              {                   // the intersection demonstrated that mutable and                   // immutable list have different counts, thus, a new                   // immutable list must be created again                  _immutableStringList = StringList.ToImmutableList();                   return _immutableStringList;              }          }      } } 
like image 173
Matías Fidemraizer Avatar answered Oct 10 '22 08:10

Matías Fidemraizer


I do not think immutable is the way to go

int[] source = new int[10000000];//uses 40MB of memory var imm1 = source.ToImmutableArray();//uses another 40MB var imm2 = source.ToImmutableArray();//uses another 40MB 

List behaves the same way. If I want to make full copy every time, I do not have to care about what user does with that array. Making it immutable does not protect content of objects in the collection either, they can be changed freely. @HansPassant suggestion seems to be best

public class A {     protected List<int> list = new List<int>(Enumerable.Range(1, 10000000));     public IReadOnlyList<int> GetList     {         get { return list; }     } } 
like image 35
Antonín Lejsek Avatar answered Oct 10 '22 07:10

Antonín Lejsek