title: Using ReadOnlyCollection<T> vs List<T> vs array for return types
body: <p>I posted a <a href="https://stackoverflow.com/q/41264238/197591">similar question</a> a while ago concerning parameters, and it referred to <a href="http://www.fascinatedwithsoftware.com/blog/post/2012/07/22/LINQ-Entices-Liskov-Violations.aspx" rel="nofollow noreferrer">an article</a> more pertinent to <em>this</em> question, which advocates using <code>IReadOnlyCollection&lt;T&gt;</code> over <code>IEnumerable&lt;T&gt;</code>.</p>
  <blockquote>
  <p>Lately, I’ve been considering the merits and demerits of returning
  <code>IEnumerable&lt;T&gt;</code>.</p>
  <p>On the plus side, it is about as minimal as an interface gets, so it
  leaves you as method author more flexibility than committing to a
  heavier alternative like <code>IList&lt;T&gt;</code> or (<a href="http://msdn.microsoft.com/en-us/library/0fss9skc.aspx" rel="nofollow noreferrer">heaven forbid</a>) an array.</p>
  <p>However, as I outlined in <a href="http://www.fascinatedwithsoftware.com/blog/post/2012/07/22/LINQ-Entices-Liskov-Violations.aspx" rel="nofollow noreferrer">the last post</a>, an <code>IEnumerable&lt;T&gt;</code> return
  entices callers to violate the <a href="http://www.fascinatedwithsoftware.com/blog/post/2012/04/08/Refactoring-to-SOLID-Part-4-The-Liskov-Substitution-Principle.aspx" rel="nofollow noreferrer">Liskov Substitution Principle</a>. It’s too
  easy for them to use LINQ extension methods like <code>Last()</code> and <code>Count()</code>,
  whose semantics <code>IEnumerable&lt;T&gt;</code> does not promise.</p>
  <p>What’s needed is a better way to lock down a returned collection
  without making such temptations so prominent. (I am reminded of Barney
  Fife learning this lesson the hard way.)</p>
  <p>Enter <a href="http://msdn.microsoft.com/en-us/library/hh881542(v=vs.110).aspx" rel="nofollow noreferrer"><code>IReadOnlyCollection&lt;T&gt;</code></a>, new in .NET 4.5. It adds just one
  property to <code>IEnumerable&lt;T&gt;</code>: the <code>Count</code> property. By promising a count,
  you assure your callers that your <code>IEnumerable&lt;T&gt;</code> really does have a
  terminus. They can then use LINQ extension methods like <code>Last()</code> with a
  clear conscience.</p>
  </blockquote>
  <p>However, <a href="https://web.archive.org/web/20201027094654/http://geekswithblogs.net/BlackRabbitCoder/archive/2010/11/04/c.net-fundamentals-returning-an-immutable-collection.aspx" rel="nofollow noreferrer">this article</a> advocates using <code>ReadOnlyCollection&lt;T&gt;</code> (and other alternatives) for the case where you want to protect members.</p>
  <p>So, I discussed this with a colleague, and we have different opinions.</p>
  <p>He suggests that a method should return whatever it has (such as a <code>List&lt;T&gt;</code> or array) if the method produced it and the caller changing it afterwards would have no effect on anything else (i.e., it's not a member and doesn't belong to anything whose lifetime is greater than the method call).</p>
  <p>My view is that <code>ReadOnlyCollection&lt;T&gt;</code> indicates to the caller that the collection should typically remain as is. If they want to change it, then the onus is on them to explicitly cast it to a <code>IList&lt;T&gt;</code> or call <code>ToList</code> on it.</p>
  <p>The <a href="https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/guidelines-for-collections" rel="nofollow noreferrer">Microsoft guidelines</a> in particular state:</p>
  <blockquote>
  <p>In general, prefer <code>ReadOnlyCollection&lt;T&gt;</code>.</p>
  </blockquote>
  <p>But it doesn't seem to define the general case other than stating to use <code>Collection&lt;T&gt;</code> for returning read/write collections. But is my colleague's view that the consumer <em>might</em> want to add to the collection and that we don't care if they do the definition for returning a read/write collection, or not?</p>
  <p><strong>EDIT</strong></p>
  <p>In answer to some of the replies, to try and add more context to my question, I've placed some code below demonstrate various scenarios with comments relating to my question:</p>
  <pre><code>class Foo
  {
      private readonly int[] array = { 1 };
      private readonly List&lt;int&gt; list = new List&lt;int&gt;(new[] {1});
  
      // Get member array.
      public int[] GetMemberArrayAsIs() =&gt; array;
  
      public IEnumerable&lt;int&gt; GetMemberArrayAsEnumerable() =&gt; array;
  
      public ReadOnlyCollection&lt;int&gt; GetMemberArrayAsReadOnlyCollection() =&gt; new ReadOnlyCollection&lt;int&gt;(array);
  
      // Get local array.
      public int[] GetLocalArrayAsIs() =&gt; new[] { 1 };
  
      public IEnumerable&lt;int&gt; GetLocalArrayAsEnumerable() =&gt; new[] { 1 };
  
      public ReadOnlyCollection&lt;int&gt; GetLocalArrayAsReadOnlyCollection() =&gt; new ReadOnlyCollection&lt;int&gt;(new[] { 1 });
  
      // Get member list.
      public Collection&lt;int&gt; GetMemberListAsIs() =&gt; new Collection&lt;int&gt;(list);
  
      public IEnumerable&lt;int&gt; GetMemberListAsEnumerable() =&gt; array;
  
      public ReadOnlyCollection&lt;int&gt; GetMemberListAsReadOnlyCollection() =&gt; new ReadOnlyCollection&lt;int&gt;(array);
  
      // Get local list.
      public Collection&lt;int&gt; GetLocalListAsIs() =&gt; new Collection&lt;int&gt;(new[] { 1 });
  
      public IEnumerable&lt;int&gt; GetLocalListAsEnumerable() =&gt; new List&lt;int&gt;(new[] { 1 });
  
      public ReadOnlyCollection&lt;int&gt; GetLocalListAsReadOnlyCollection() =&gt; new List&lt;int&gt;(new[] { 1 }).AsReadOnly();
  }
  
  
  class FooTest
  {
      void Test()
      {
          var foo = new Foo();
          int count;
  
          // Get member array.
          var array1 = foo.GetMemberArrayAsIs(); // ReSharper encourages to make the return type IEnumerable&lt;T&gt;.
          count = array1.Length;  // ...unless we do this.
  
          var enumerable1 = foo.GetMemberArrayAsEnumerable();
          enumerable1.Concat(enumerable1); // Warning of possible multiple enumeration.
  
          var roc1 = foo.GetMemberArrayAsReadOnlyCollection(); // ReSharper encourages to make the return type IEnumerable&lt;T&gt;.
          count = roc1.Count;  // ...unless we do this.
  
  
          // Get local array.
          var array2 = foo.GetLocalArrayAsIs(); // ReSharper encourages to make the return type IEnumerable&lt;T&gt;.
          count = array2.Length;  // ...unless we do this.
  
          var enumerable2 = foo.GetLocalArrayAsEnumerable();
          enumerable2.Concat(enumerable2); // Warning of possible multiple enumeration.
  
          var roc2 = foo.GetLocalArrayAsReadOnlyCollection(); // ReSharper encourages to make the return type IEnumerable&lt;T&gt;.
          count = roc2.Count;  // ...unless we do this.
  
  
          // Get member list.
          var list1 = foo.GetMemberListAsIs(); // ReSharper encourages to make the return type IEnumerable&lt;T&gt;.
          count = list1.Count;  // ...unless we do this.
          list1.Add(2); // This affects the Foo object as the collection is a member. DANGEROUS!
  
          var enumerable3 = foo.GetMemberListAsEnumerable();
          enumerable3.Concat(enumerable3); // Warning of possible multiple enumeration.
  
          var roc3 = foo.GetMemberListAsReadOnlyCollection(); // ReSharper encourages to make the return type IEnumerable&lt;T&gt;.
          count = roc3.Count;  // ...unless we do this.
  
  
          // Get local list.
          var list2 = foo.GetLocalListAsIs(); // ReSharper encourages to make the return type IEnumerable&lt;T&gt;.
          count = list2.Count;  // ...unless we do this.
          list2.Add(2); // This doesn't affect the Foo object as the collection was produced by the method.
  
          var enumerable4 = foo.GetLocalListAsEnumerable();
          enumerable4.Concat(enumerable4); // Warning of possible multiple enumeration.
  
          var roc4 = foo.GetLocalListAsReadOnlyCollection(); // ReSharper encourages to make the return type IEnumerable&lt;T&gt;.
          count = roc4.Count;  // ...unless we do this.
      }
  }
  </code></pre>
  <p>So to use an example to demonstrate the dilemma, if we take <code>GetMemberArrayAsIs</code>, ReSharper tells me to change the return type to <code>IEnumerable&lt;T&gt;</code> if the calling code enumerates the returned value only once. If I enumerate the returned value more than once, then I must leave it as it is in order to avoid the possible multiple enumeration warning.  However, this is where my question comes in.  Instead of returning the array, should I return <code>ReadOnlyCollection&lt;T&gt;</code> to comply with my understanding of the Microsoft guidelines?  This would require me to instantiate a <code>ReadOnlyCollection&lt;T&gt;</code> as shown in <code>GetMemberArrayAsReadOnlyCollection</code>.  Or, should we just return the array as is as my colleague believes (<code>GetMemberArrayAsIs</code>)?</p>
  <p>Alternatively, I could insist on returning <code>IEnumerable&lt;T&gt;</code> and put the onus on the caller to enumerate before using the collection multiple times, but I think Microsoft's preference to use <code>ReadOnlyCollection&lt;T&gt;</code> is to avoid exactly this.</p>
  <p>In the case of a <code>List&lt;T&gt;</code>, the dilemma is slightly more complicated because of the possibility it can be changed which is a problem if it's a member.  In this case, I should certainly return a <code>ReadOnlyCollection&lt;T&gt;</code> (<code>GetMemberListAsReadOnlyCollection</code>).  But in the case of a local list, should I return <code>ReadOnlyCollection&lt;T&gt;</code> to comply with my understanding of the Microsoft guidelines?  This would require me to instantiate a <code>ReadOnlyCollection&lt;T&gt;</code> as shown in <code>GetLocalListAsReadOnlyCollection</code>.  Or, should we just return the list as is as my colleague believes (<code>GetMemberListAsIs</code>)?</p>
  <p>I hope this adds more context to the question, and it's fairly philosophical in some respects.  But a lot of it is about how the Microsoft guidelines should be interpreted, and whether the onus should be put on the provider to convert to <code>ReadOnlyCollection&lt;T&gt;</code>, or for the consumer to enumerate the returned value before using multiple times (using <code>ToArray</code>), or indeed neither and returning as is.</p>