[ACCEPTED]-Ways of creating a constant IEnumerable<TSomeType>...?-c#-3.0

Accepted answer
Score: 10

Well, neither List<T> nor arrays are immutable, so 34 they're out if you're really after immutability 33 - the caller could cast the result and then 32 modify it.

You could create a List<T> and wrap that 31 in a ReadOnlyCollection<T>. If nothing has a reference to the 30 original list any more, then it's effectively 29 immutable, barring reflection.

If you don't 28 actually care about immutability - i.e. if 27 you trust all the code not to mess with 26 it - then an array is going to be the most 25 performant approach, almost certainly. There 24 are various CLR-level optimizations which 23 make them work blazingly fast. However, in 22 that case I wouldn't cast to IEnumerable<T> - I'd just expose 21 it as an array. That will make it faster 20 to iterate over than if the compiler has 19 to call GetEnumerator().

If the C# compiler sees a foreach statement 18 over an array, it generates calls to go 17 straight to the indexer and use the Length property... and 16 then the CLR will also be able to remove 15 bounds checking, spotting the pattern.

Likewise 14 if you decide to go with List<T>, leave it as a 13 List<T> - that way you'll get to use List<T>.Enumerator - which is 12 a struct - directly, without boxing.

EDIT: Steve 11 Megson brings up the point of using LINQ 10 for this. Actually, you can probably do 9 better than that, because once you've got 8 the enumerator of the underlying list, you 7 can give that back to the caller safely, at 6 least for all collections I'm aware of. So 5 you could have:

public class ProtectedEnumerable<T> : IEnumerable<T>
{
    private readonly IEnumerable<T> collection;

    public ProtectedEnumerable(IEnumerable<T> collection)
    {
        this.collection = collection;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return collection.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

That means there's only a 4 tiny hit when iterating - just the single delegated 3 call to GetEnumerator(). Compare that with using Enumerable.Select, which 2 will need to take an extra delegation hit 1 on every call to MoveNext() (as well as the no-op projection).

Score: 6

While Jon has answered the question on making the list immutable, I 27 would like to also point out that even if 26 the list is immutable, its contained objects 25 are not automatically immutable.

Even if 24 you make the list immutable (for instance 23 by copying its contents into a ReadOnlyCollection<T>), you can still manipulate properties of the contained objects, if they are not of an immutable type. As soon 22 as you pass out references to the objects 21 contained in the list, calling code can 20 manipulate those objects.

Let's take an example:

class Person
{
    public string Name { get; set; }
}
class Group
{
    private readonly IEnumerable<Person> _persons;
    public Group(IEnumerable<Person> persons)
    {
        _persons = new ReadOnlyCollection<Person>(persons.ToList());
    }
    public IEnumerable<Person> Persons
    {
        get
        {
            foreach (var person in _persons)
            {
                yield return person;
            }
        }
    }
}

Then 19 we have the following code:

List<Person> persons = new List<Person>( new[]{new Person { Name = "Fredrik Mörk" }});
Group smallGroup = new Group(persons);
Console.WriteLine("First iteration");
foreach (var person in smallGroup.Persons)
{
    Console.WriteLine(person.Name);
    person.Name += " [altered]";
}
Console.WriteLine("Second loop");
foreach (var person in smallGroup.Persons)
{
    Console.WriteLine(person.Name); // prints "Fredrik Mörk [altered]"
}

As you can see, even 18 though we have make the list effectively immutable, *Person is 17 not an immutable type. Since the Persons property 16 of the Group class passes out references to the 15 actual Person objects, the calling code can easily 14 manipulate the object.

One way to protect 13 yourself against this is to expose the collection 12 as an IEnumerable<T> using yield return and some cloning mechanism 11 to make sure that you don't pass out the 10 original object references. For example, you 9 can alter the Person class into this:

class Person
{
    public string Name { get; set; }

    // we add a copy method that returns a shallow copy...
    public Person Copy()
    {
        return (Person)this.MemberwiseClone();
    }   
}

class Group
{
    private readonly IEnumerable<Person> _persons;
    public Group(IEnumerable<Person> persons)
    {
        _persons = new ReadOnlyCollection<Person>(persons.ToList());
    }
    public IEnumerable<Person> Persons
    {
        get
        {
            foreach (var person in _persons)
            {
                // ...and here we return a copy instead of the contained object
                yield return person.Copy();
            }
        }
    }
}

Now, the 8 program above will not alter the name of the 7 Person instance inside the list, but its own copy. However, note 6 that we now have what can be called shallow immutability: if 5 Person in turn would have members that are not 4 immutable, the same problem exists for those 3 objects, and so on...

Eric Lippert wrote 2 a 10-part series of blog post on the topic 1 in 2007. The first part is here: Immutability in C# Part One: Kinds of Immutability.

Score: 5

If it's constant, I'd go with the array 4 as there's no need for the extra functionality 3 a list provides for adding and removing 2 items. Array will have a minimal memory 1 footprint too.

Score: 2

You could take a look at ReadOnlyCollection<T> which wraps an 2 existing IList<T> up as read-only and can be 1 cast to IEnumerable<T>

http://msdn.microsoft.com/en-us/library/ms132474.aspx

Score: 1

AFAIK, because of the less complex structure, the 6 rule would be: If you need to increase or 5 decrease the size of the collection, use 4 list, if not and you are only using it for 3 enumeration, use array.

I don't think constant 2 vs regular variable makes much of a different 1 in the instantiation of the object.

More Related questions