Why does a collection initializer expression require IEnumerable to be implemented?
Asked Answered
M

1

34

Why does this generate a compiler error:

class X { public void Add(string str) { Console.WriteLine(str); } }

static class Program
{
    static void Main()
    {
        // error CS1922: Cannot initialize type 'X' with a collection initializer
        // because it does not implement 'System.Collections.IEnumerable'
        var x = new X { "string" };
    }
}

but this doesn’t:

class X : IEnumerable
{
    public void Add(string str) { Console.WriteLine(str); }
    IEnumerator IEnumerable.GetEnumerator()
    {
        // Try to blow up horribly!
        throw new NotImplementedException();
    }
}

static class Program
{
    static void Main()
    {
        // prints “string” and doesn’t throw
        var x = new X { "string" };
    }
}

What is the reason for restricting collection initializers — which are syntactic sugar for a call to an Add method — to classes that implement an interface which doesn’t have an Add method and which isn’t used?

Meingoldas answered 15/9, 2010 at 10:8 Comment(6)
There are some things in C# I just dont get. This is one of them. Another is foreach.Dimorphism
@leppie: What don't you "get" about foreach?Nic
@Jon Skeet: I think leppie refers to the fact that the compiler simply requires the presence of a method without requiring a specific interface which is sort of duck-typing behaviour in a strongly-typed language.Recto
@0xA3: That's mostly explained by the lack of generics in C# 1. This duck typing allowed for strongly typed iterators using value types without boxing. I suspect it wouldn't be present in C# if it had generics to start with, although it's still used to avoid boxing the iterator when you use foreach over a List<T>, for example.Nic
@Jon Skeet: The fact that it does more than one expects. I do 'get' it, I just dont get why. But as you said, it could be to do with generics.Dimorphism
related: stackoverflow.com/questions/459652/…Vainglory
N
29

An object initializer doesn't; a collection initializer does. It's so that it's applied to classes which really represent collections, rather than just arbitrary ones which have an Add method. I have to admit that every so often I've "implemented" IEnumerable explicitly, just to allow collection initializers - but thrown a NotImplementedException from GetEnumerator().

Note that early in C# 3's development, collection initializers had to implement ICollection<T>, but that was found to be too restrictive. Mads Torgersen blogged about this change, and the reason behind requiring IEnumerable, back in 2006.

Nic answered 15/9, 2010 at 10:13 Comment(3)
Wouldn’t the same argument apply to the LINQ query syntax? Yet it doesn’t require any interfaces, and even allows the “iterator variable” to be a type name and “Where/Select” to be a static method...Meingoldas
@Timwi: Sort of, although it's less likely that a query expression will actually compile against an arbitrary type than a collection initializer - I suspect that Add has more "other meanings" than Where and Select methods with appropriate parameter types. Also note that there isn't one appropriate interface which could have been applied to sanity-check LINQ queries - think about Reactive Extnesions, which doesn't have any common interface with LINQ to Objects...Nic
Not a day goes by without me wishing .NET 2 was the first version... 90% of all the cruft in .NET seems to come from the existence of .NET 1 with its non-generic stuff...Gut

© 2022 - 2024 — McMap. All rights reserved.