September, 2006

  • Eric White's Blog

    Notes on Implementing the Visitor Pattern using Extension Methods

    • 1 Comments

    [Blog Map]  This blog is inactive.  New blog: EricWhite.com/blog

    Well, I've checked with one of the experts, Dr. Ralf Lämmel, and he confirmed: as soon as you use a StringBuilder, it's not pure. So example 4 in LINQ to XML: Implementing the Visitor Pattern using Extension Methods can't be said to be pure.

    However, I think that in terms of analysis of queries with an eye to determining if they could be executed in separate threads in PLINQ, using the StringBuilder like this would work.

    Also, Ralf made a good point about using the Aggregate standard query operator: In the sample, we're creating a StringBuilder and passing it in as the seed to Aggregate. Then, we're taking advantage of the fact that StringBuilder.Append returns the same StringBuilder, returning that to Aggregate, so that it gets passed back into the lambda with each iteration. StringBuilder.Append returns the StringBuilder so that you can do method chaining, appending multiple strings to the same StringBuilder. In Aggregate, the purpose of the seed is to pass in some value that then gets passed the lambda. Then each return value of the lambda is passed in to the next invocation of the lambda. It is really intended to be used something like this:

    int [] ia = { 1, 5, 3, 4 };
    var sum = ia.Aggregate(0, (s, i) => s += i);
    Console.WriteLine(sum);

    So examples 2 and 4 use Aggregate in a way it wasn’t really intended, and they use the return value of StringBuilder.Append in a way it wasn’t really intended.

    But it all works. Hmmm. I'm looking for some feedback here. Is it a recommended style to use Aggregate in this fashion?

    Example 3 uses Aggregate in the spirit it was intended to be used.

    One more note: I fixed a couple of bugs in the samples, changing example 3 from:

    string str =
        purchaseOrder.Visit(
            (object o, int depth) =>
                String.Format("{0}{1}{2}",
                    "".PadRight(depth * 4),
                    (GetName(o) + ": ").PadRight(14),
                    GetLeafValue(o)
                )
        )
        .Aggregate((s, t) => s + t + Environment.NewLine);
    Console.WriteLine(str);

    To this:

    string str =
        purchaseOrder.Visit(
            (object o, int depth) =>
                String.Format("{0}{1}{2}",
                    "".PadRight(depth * 4),
                    (GetName(o) + ": ").PadRight(14),
                    GetLeafValue(o)
                )
        )
        .Aggregate("", (s, t) => s + t + Environment.NewLine);
    Console.WriteLine(str);

    The difference is that the empty string is passed as the seed to Aggregate, which is the correct use of it.

Page 1 of 5 (5 items) 12345
Page 1 of 1 (5 items)