views:

83

answers:

1

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?

+5  A: 

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.

Jon Skeet
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...
Timwi
@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...
Jon Skeet
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...
romkyns