Chain<T>

I have found this class useful so I figured that I would share it. It’s main use is a quick lightweight fluent interface for creating an IEnumerable<T> in a test. The place where it excels is when we want to do something like have a nice fluent interface for adding things to another class. A while back I saw an example of this on Jeff’s blog.

The particular example was:

 108: [Test]
109: public void ShouldInitializeWithKeys()
110: {
111: SmartBag bag = new SmartBag().Add("key1", 2).Add("key2", 3);
112: Assert.That(bag.ContainsKey("key1"));
113: Assert.That(bag.ContainsKey("key2"));
114: }

What he had done to support this nice fluent interface for adding items was to make the SmartBag return itself from the add method (This pattern is quite common for value objects but in this case SmartBag was actually mutating itself then returning itself). I had a place or two like this in code and changed them to use the Chain<T> class by just making the class with the add support taking an IEnumerable<T> (kill two birds with one stone).

WhatEver.Add(Chain<int>.Create(1,2,3,4,5))

WhatEver.Add(

             Chain<SomeObject>.Empty

                    .Append(New.SomeObject.WithFluentInterface)

                    .Append(New.SomeObject.WithFluentInterface)

                    .Append(New.SomeObject.WithFluentInterface)

                    .Append(New.SomeObject.WithFluentInterface)

                    .Append(New.SomeObject.WithFluentInterface)

);

Personally I find myself using the From method more to initially create them but the append can be useful as well.

This code is also an example of an immutable data structure. You can access it here http://codekeep.net/snippets/6a941ef6-88ff-4903-b0fd-7ca261ef3bd3.aspx or view it below.

 

One thing I have really wanted to add to it is support for adding a node of IEnumerable<T> which would then iterate that node (i.e. so you could take 5 enumerarables and combine them into 1 without actually evaluating them). Maybe I will do that tomorrow.

 

    public class Chain : IEnumerable, IEnumerable
    {
        private static readonly Chain empty = new Chain(default(T), null);
        public static Chain Empty
        {
            get { return empty; }
        }

        public static Chain From(T t)
        {
            return Empty.Append(t);
        }

        public static Chain From(IEnumerable _Items)
        {
            return Empty.Append(_Items);
        }

        public static Chain From(params T[] items)
        {
            return Empty.Append(items);
        }

        private readonly T item;
        private readonly Chain next;

        private IEnumerable GetItemsRecursive()
        {
            if (next != null) {
                foreach (T child in next.GetItemsRecursive()) yield return child;
                yield return item;
            }
        }

        public Chain Append(T t)
        {
            return new Chain(t, this);
        }

        public Chain Append(IEnumerable _Items)
        {
            if (_Items == null) throw new ArgumentNullException("Items");
            Chain current = this;
            foreach (T cur in _Items) {
                current = current.Append(cur);
            }
            return current;
        }

        public Chain Append(params T[] items)
        {
            return Append((IEnumerable) items);
        }

        private Chain(T _Item, Chain _Next)
        {
            item = _Item;
            next = _Next;
        }

        public IEnumerator GetEnumerator()
        {
            return GetItemsRecursive().GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetItemsRecursive().GetEnumerator();
        }
    }
This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

5 Responses to Chain<T>

  1. Greg says:

    Yes Jon it does make it pretty easy to do … For this project we are still in 2.0 though. I would say that the two have pretty much an equivalent “easiness” though with the From() creation methods.

    Cheers,

    Greg

  2. Jon Skeet says:

    Doesn’t the C# 3 collection initializer syntax make this straightforward anyway? That’s a simple way of creating a collection with multiple items in a single expression. For example:

    List items = new List
    {
    “first”,
    “second”,
    “third”
    };

    The important bit is that it’s a single expression – which is what Chain achieves as well, but I find the C# syntax slightly simpler, and you don’t get the nasty recursion issue when iterating.

    Am I missing something?

  3. Greg says:

    Oren: yes you would get a recursion error on that but then again I would never under any circumstances recommend doing that :-) The place where I am using this is for manually inputting test data .. In the scenario you discuss it would be creating 100,001 objects which is also really a bad thing (compared to say an array which would use 2) or using a generator function which would use 1.

    Mark: by the create method I mean From

  4. Mark says:

    I think something is missing from the code. I don’t see the Create(..) method that you refer to. Did all the code get posted?

  5. Chain c = Chain.Empty;
    for(i=0;i<100000;i++)
    c = c.Append(i);

    foreach(i in c)//stack overflow, right?
    {

    }

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>