40

What's the standard way to get a typed, readonly empty list in C#, or is there one?

ETA: For those asking "why?": I have a virtual method that returns an IList (or rather, post-answers, an IEnumerable), and the default implementation is empty. Whatever the list returns should be readonly because writing to it would be a bug, and if somebody tries to, I want to halt and catch fire immediately, rather than wait for the bug to show up in some subtle way later.

David Moles
  • 48,006
  • 27
  • 136
  • 235
  • 4
    What will you do with a readonly empty list anyway? Just curious. – goenning Oct 08 '10 at 22:42
  • I'm guessing an empty `IEnumerable` is probably the right answer here - is a IList really needed? or just an empty readonly collection? – Reed Copsey Oct 08 '10 at 22:44
  • 1
    just curious, why do you need an empty readonly list? – ps. Oct 08 '10 at 22:47
  • `IEnumerable` works fine in this case, thanks. – David Moles Oct 08 '10 at 23:25
  • Re: why, see edited question above. – David Moles Oct 08 '10 at 23:33
  • I think it is a bit strange that you need to return empty IList. In functional terms, a list is an sequence. However, in C# a IList is something that you CAN add to. I suspect that you should be returning Enumerable.Empty(), which will give an empty sequence. – Tormod Aug 07 '12 at 08:25
  • 2
    If an `IList` was just a sequence, we wouldn't need it, we'd just use `IEnumerable`. You need at least `ICollection` if you want any of the `ICollection` methods and `IList` if you want indexed access. The modification operations in `ICollection` are optional, which is why there's `IsReadOnly()` and why `Add()` etc. are documented to throw `NotSupportedException`. – David Moles Aug 15 '12 at 17:41

8 Answers8

28

Personally, I think this is better than any of the other answers:

static readonly IList<T> EmptyList = new T[0];
  • Arrays implement IList<T>.
  • You cannot add to an array.
  • You cannot assign to an element in an empty array (because there is none).
  • This is, in my opinion, a lot simpler than new List<T>().AsReadOnly().
  • You still get to return an IList<T> (if you want).

Incidentally, this is what Enumerable.Empty<T>() actually uses under the hood, if I recall correctly. So theoretically you could even do (IList<T>)Enumerable.Empty<T>() (though I see no good reason to do that).

Dan Tao
  • 125,917
  • 54
  • 300
  • 447
  • Your answer really only makes sense to me in terms of the comments attached to [Virtlink's answer](https://stackoverflow.com/a/10659068/712526). – jpaugh Jun 05 '17 at 20:20
  • 5
    Since 4.6 you don't even need to provide your own object anymore, just return Array.Empty(). – ZunTzu Nov 08 '17 at 14:05
26

You can just create a list:

List<MyType> list = new List<MyType>();

If you want an empty IEnumerable<T>, use Enumerable.Empty<T>():

IEnumerable<MyType> collection = Enumerable.Empty<MyType>();

If you truly want a readonly list, you could do:

IList<MyType> readonlyList = (new List<MyType>()).AsReadOnly();

This returns a ReadOnlyCollection<T>, which implements IList<T>.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • 2
    I thought this too but then saw the readonly requirement. Then I thought what would be the point of an empty readonly List. What am I missing? – Jay Riggs Oct 08 '10 at 22:40
  • 3
    @JayR An empty, readonly collection is ideal for an API return value, in order to convey the idea of "no results" without resorting to `null`. And, ideally, you'd want to refer to the same readonly, empty collection for the same reason you'd want to use `string.Empty`: memory conservation. – jpaugh Jun 05 '17 at 19:54
17

Starting with .net 4.6 you can also use:

IList<T> emptyList = Array.Empty<T>();

This does only create a new instance once for every different type you specify as T.

haindl
  • 3,111
  • 2
  • 25
  • 31
  • 1
    Just like String.Empty, which also always references the same instance instead of creating a new empty string. – David De Sloovere Nov 29 '16 at 17:26
  • 3
    @David De Sloovere the empty string literal "" also references the same instance thanks to string interning (just telling for readers who would feel obliged to replace "" by String.Empty). – ZunTzu Nov 08 '17 at 14:09
9
IList<T> list = new List<T>().AsReadOnly();

Or, if you want an IEnumerable<>:

IEnumerable<T> sequence = Enumerable.Empty<T>();
LukeH
  • 263,068
  • 57
  • 365
  • 409
4

If you want a list whose contents can't be modified, you can do:

ReadOnlyCollection<Foo> foos = new List<Foo>().AsReadOnly();
Bryan Watts
  • 44,911
  • 16
  • 83
  • 88
2

To expand on Dan Tao's answer, the following implementation can be used in the same way as Enumerable.Empty<T>(), by specifying List.Empty<T>() instead.

public static class List
{
    public static IList<T> Empty<T>()
    {
        // Note that the static type is only instantiated when
        // it is needed, and only then is the T[0] object created, once.
        return EmptyArray<T>.Instance;
    }

    private sealed class EmptyArray<T>
    {
        public static readonly T[] Instance = new T[0];
    }
}

Edit: I change the above code to reflect the outcome of a discussion with Dan Tao about lazy versus eager initialization of the Instance field.

Community
  • 1
  • 1
Daniel A.A. Pelsmaeker
  • 47,471
  • 20
  • 111
  • 157
  • Lazy initialization of a zero-length array? I personally think that's a mistake, as it adds complexity, will make performance slightly worse (extra method call on every access), has a race condition (two threads calling `List.Empty()` at the same time could get different objects) and buys you almost nothing (how do you think the initialization cost of a single zero-length array compares to that of JIT compiling a new class?). Go with an eagerly-initialized `static readonly` public field, or--if your conscience won't allow it--a private field plus the `Empty` method. Just my two cents! – Dan Tao May 18 '12 at 20:28
  • 1
    Two threads getting different objects is not an issue. Performance is not affected as the call is [very likely inlined](https://blogs.msdn.com/b/ericgu/archive/2004/01/29/64717.aspx). I don't see how one line of actual logic could severely add to complexity. And last but not least: I just checked the implementation of `Enumerable.Empty()` and it is very similar to what I wrote. If one needs empty lists of all types all over the place, use this approach as it saves allocations and garbage. If one only rarely needs an empty list, use your `static readonly`. – Daniel A.A. Pelsmaeker May 18 '12 at 22:12
  • You're probably right to be defensive, as my comment was certainly splitting hairs. However, different objects could be an issue if, for example, you had some critical code that depended on the check `if (list == List.Empty())`. Also, how does this approach save on allocations and garbage? – Dan Tao May 18 '12 at 22:22
  • Okay my approach won't save garbage compared to yours. But it will save allocations: any time you write `static readonly IList EmptyList = new T[0]` you allocate another `T[0]` object. If you use _n_ different types T you will allocate _at least n_ `T[0]` objects, whereas my approach will allocate exactly _n_ `T[0]` objects. By the way, a programmer should never rely on `list == List.Empty()` (or `e == Enumerable.Empty()` as it has the same issue). `Empty()` is a method after all, and methods normally don't guarantee always returning the same value. – Daniel A.A. Pelsmaeker May 18 '12 at 22:38
  • I think you're missing something; `static readonly IList EmptyList = new T[0]` will only ever be called once per `T` by virtue of the fact that it is **static** (and thus could only *exist* in a type's static constructor or static field declarations). Thus the number of *allocations* between the lazy-initialization and eager-initialization approaches should be identical. In my opinion, the only real reason to go with lazy initialization is when you want to save on startup time and the initialization cost is significant. This is not that case, hence my resistance to your idea. – Dan Tao May 18 '12 at 22:44
  • As I said, I would indeed go with your approach for when I need very few empty lists in very few different places. If I understood you correctly, you are wrong: the fact that its static means that it will be instantiated once per declaration in a class. If you declare `static new Int[0]` in three different classes (or three times in a class) you get three instances for the same type `Int` (so it is _not_ called once per `T`). If you call `Empty()` in three or more different places, you allocate only one `Int[0]` ever. – Daniel A.A. Pelsmaeker May 18 '12 at 22:51
  • OK, I see where we are misunderstanding each other. I am not proposing to have a `static readonly` field in every class that needs access to an empty list; I actually *agree* with you on the usefulness of using one static class as the access point (hence one allocation per `T`). Where I disagree with you is on the lazy initialization. See here to understand what I mean: http://pastebin.com/wuHmNtZd – Dan Tao May 18 '12 at 23:15
  • 1
    Okay, right. You have shown that directly accessing a static field is twice faster (on my PC) than accessing it through a method in another class. While it is not really about lazy initialization, your initial argument about performance is correct. Obviously premature optimizations should never be an argument, but I'm starting to warm up to your eager approach. I tested it a bit, and concluded that either way the types are created no earlier than when they are needed for the first time. Why do you think the .NET framework developer also chose the lazy approach? – Daniel A.A. Pelsmaeker May 18 '12 at 23:56
2

Construct an instance of System.Collections.ObjectModel.ReadOnlyCollection from your list.

List<int> items = new List<int>();
ReadOnlyCollection<int> readOnlyItems = new ReadOnlyCollection<int>(items);
Richard Cook
  • 32,523
  • 5
  • 46
  • 71
-1

What about:

readonly List<T> mylist = new List<T>();

Not sure why you want it readonly; that doesn't make much sense in most scenarios I can think of, though.

CesarGon
  • 15,099
  • 6
  • 57
  • 85