views:

214

answers:

3

In C#, there's the "standard" initializer technique { Property1 = "a", Property2 = "b" }, and there are a couple of special variants for collections (list and dictionary). {value1, value2}, and { {"key1", value1 }, {"key2", value2} }.

I'd like to have a recursive object initializer for a tree data type, but I don't know if there's any way to customize that mechanism. I'd like something that looks like an s-expression. { item1 {item2 item3 item4 } {item5 item6} }

I'm doing this via constructors, but I'd like a terser syntax.

+4  A: 

Using a variadic local lambda n that simply calls your constructor, you could get it as short as:

n(item1, n(item2, item3, item4), n(item5, item6))

Update: something like

var n = (params Node[] nodes) => new Node(nodes);
Thomas
Why not just say params? This is a C# question.
ChaosPandion
Quick response! That's a pretty interesting solution, but it would require defining the lambda whenever I want to perform initialization. Also, it would require that all the variadic parameters are either items or trees of items, not both mixed (e.g. n(item1, n(item2, item3), item4) wouldn't be possible.
darthtrevino
You can define the lambda once and create a local reference to it when you need the shorter syntax. Helps a little bit. If your tree items are not nodes themselves, you can modify the lambda to accept a general `object[]` and do some magic on it.
Thomas
+6  A: 

If you implement the ICollection IEnumerable interface and have a method called add. Incidentally, this is all included in the ICollection interface which is why I confused it.

Test test = new Test() {
    new Test2() {
     new Test3() {

     }
    },
    new Test() {
     new Test2() {
      { new Test(), new Test2() },
      { new Test(), new Test2() },
      { new Test(), new Test2() }
     }
    }
};

public class Test : IEnumerable
{
    public void Add(Test a){}
    public void Add(Test2 a){}
    public IEnumerator GetEnumerator(){}
}

public class Test2 : IEnumerable
{
    public void Add(Test a, Test2 b){}
    public void Add(Test3 a){}
    public IEnumerator GetEnumerator(){}
}

public class Test3 : IEnumerable
{
    public void Add(Test a) {}
    public void Add(Test2 a){}
    public IEnumerator GetEnumerator(){}
}
ChaosPandion
You don't need to implement ICollection - just IEnumerable.
Jon Skeet
That's not quite as terse as what I was imagining, but it looks like it's as close as you can get. It looks like the answer to this is that you can't really customize the object initialization mechanism, but you can piggyback on the built-in IEnumerable initialization support.
darthtrevino
Found a blog post discussing this http://resharper.blogspot.com/2007/09/c-30-collection-initializers-incomplete.html
darthtrevino
So, looking closer at your post, I noticed that the Add method is key. Accepted! (although it's not quite as terse as i hoped)
darthtrevino
A: 

If the nodes of your tree are easy to construct, i.e. can be initialized from their value, then you can make things terser than ChaosPandion's answer, by adding an extra method:

class Tree : IEnumerable
{
    public string Value { get; set; }

    public void Add(Tree t) { ... }

    // Add this method
    public void Add(string s)
    {
        Add(new Tree { Value = s });
    }

    public IEnumerator GetEnumerator() { ... }
}

So:

{ item1 { item2 item3 item4 } { item5 item6 } }

Becomes:

new Tree { "item1", new Tree { "item2", "item3", "item4" }, new Tree { "item5", "item6" } };
thatismatt