10

I'm writing a small data structures library in C#, and I'm running into an architectural problem. Essentially I have a class which implements the visitor pattern, and there are many possible implementations of visitors:

public interface ITreeVisitor<T, U>
{
    U Visit(Nil<T> s);
    U Visit(Node<T> s);
}

public abstract class Tree<T> : IEnumerable<T>
{
    public readonly static Tree<T> empty = new Nil<T>();
    public abstract U Accept<U>(ITreeVisitor<T, U> visitor);
}

public sealed class Nil<T> : Tree<T>
{
    public override U Accept<U>(ITreeVisitor<T, U> visitor) { return visitor.Visit(this); }
}

public sealed class Node<T> : Tree<T>
{
    public Tree<T> Left { get; set; }
    public T Value { get; set; }
    public Tree<T> Right { get; set; }

    public override U Accept<U>(ITreeVisitor<T, U> visitor) { return visitor.Visit(this); }
}

Anytime I want to pass in a visitor, I have to create a visitor class, implement the interface, and pass it in like this:

class InsertVisitor<T> : ITreeVisitor<T, Tree<T>> where T : IComparable<T>
{
    public T v { get; set; };

    public Tree<T> Visit(Nil<T> s)
    {
        return new Node<T>() { Left = Tree<T>.empty, Value = v, Right = Tree<T>.empty };
    }

    public Tree<T> Visit(Node<T> s)
    {
        switch (v.CompareTo(s.Value))
        {
            case -1: return new Node<T>() { Left = Insert(v, s.Left), Value = s.Value, Right = s.Right };
            case 1: return new Node<T>() { Left = s.Left, Value = s.Value, Right = Insert(v, s.Right) };
            default: return s;
        }
    }
}

public static Tree<T> Insert<T>(T value, Tree<T> tree) where T : IComparable<T>
{
    return tree.Accept<Tree<T>>(new InsertVisitor<T>() { v = value });
}

I don't like writing that much boilerplate code, because it gets very messy when you have a non-trivial number of visitor implementations.

I want to write something similar to anonymous classes Java (concept code):

public static Tree<T> Insert<T>(T v, Tree<T> tree) where T : IComparable<T>
{
    return tree.Accept<Tree<T>>(new InsertVisitor<T>()
        {
            public Tree<T> Visit(Nil<T> s) { return new Node<T>() { Left = Tree<T>.empty, Value = v, Right = Tree<T>.empty }; }
            public Tree<T> Visit(Node<T> s)
            {
                switch (v.CompareTo(s.Value))
                {
                    case -1: return new Node<T>() { Left = Insert(v, s.Left), Value = s.Value, Right = s.Right };
                    case 1: return new Node<T>() { Left = s.Left, Value = s.Value, Right = Insert(v, s.Right) };
                    default: return s;
                 }
            }
        };
}

Is there any way to simulate anonymous classes with interface implementations in C#?

Kevin Hakanson
  • 41,386
  • 23
  • 126
  • 155
Juliet
  • 80,494
  • 45
  • 196
  • 228
  • 2
    Might want to explain what an anonymous interface is. I'm afraid I have no clue what it means. – Noldorin Jan 15 '10 at 20:05
  • 2
    @Noldorin: anonymous *interface* isn't the best choice of words, what I mean was anonymous *class*. There's a feature in Java where you can implement interfaces on the fly without needing a named class -- I'd like to do something similar in C#. – Juliet Jan 15 '10 at 20:12
  • You sure you can't make do with delegates? – Lasse V. Karlsen Jan 15 '10 at 20:15
  • 1
    In C#, anonymous types cannot implement interfaces. See here for more information: http://msdn.microsoft.com/en-us/library/bb397696%28VS.100%29.aspx – Scott Arrington Jan 15 '10 at 20:17
  • I'm not sure if I got this right, but instead of a visitor interface, could you make a visitor base class with the common logic? – recursive Jan 15 '10 at 20:19
  • @Juliet: stop smoking F# – Mauricio Scheffer Jan 15 '10 at 20:32
  • @Mauricio: Guilty as charged ;) I'm actually trying to write an immutable data structures library in C#, maybe even start an open source project on Google code, and I'm using F#'s programming idioms as my baseline. Anonymous classes are useful because they keep all the tree traversal logic contained in the same method and (at least superficially) resembles pattern matching. – Juliet Jan 15 '10 at 20:45
  • @Juliet: how about http://code.google.com/p/functional-dotnet/ http://code.msdn.microsoft.com/BclExtras – Mauricio Scheffer Jan 15 '10 at 20:54
  • See http://stackoverflow.com/questions/508853/c-feature-request-implement-interfaces-on-anonymous-types – pedro Jan 15 '10 at 20:19

1 Answers1

7

You could change the "behavior" part of the class from methods defined against an interface to delegates called at the appropriate time, and create a new visitor by passing new delegates -- thereby enlisting anonymous functions to do the work of anonymous classes.

Sketch code (not tested, you can clean up as appropriate):

class CustomVisitor<T> : ITreeVisitor<T, Tree<T>> where T : IComparable<T>
{
    public T v { get; set; };
    public Func<Nil<T>,  Tree<T>> VisitNil  { get; set; }
    public Func<Node<T>, Tree<T>> VisitNode { get; set; }

    public Tree<T> Visit(Nil<T>  s) { return VisitNil(s);  }
    public Tree<T> Visit(Node<T> s) { return VisitNode(s); }
}

public static Tree<T> Insert<T>(T v, Tree<T> tree) where T : IComparable<T>
{
    return tree.Accept<Tree<T>>(new CustomVisitor<T> {
        VisitNil  = s =>
            return new Node<T>() { Left = Tree<T>.empty, Value = v, Right = Tree<T>.empty }; }
        VisitNode = s =>
        {
            switch (v.CompareTo(s.Value))
            {
                case -1: return new Node<T>() { Left = Insert(v, s.Left), Value = s.Value, Right = s.Right };
                case  1: return new Node<T>() { Left = s.Left, Value = s.Value, Right = Insert(v, s.Right) };
                default: return s;
             }
        }
    });
}
mqp
  • 70,359
  • 14
  • 95
  • 123
  • +1, +answer: oh wow, that was simpler than I thought :) I was worried that would need to re-write my code using `if (tree is Nil) { ... } else { ... }` just to keep all the tree traversal logic contained in the same method. Much appreciated! – Juliet Jan 15 '10 at 20:25
  • 1
    This is a good example of how functional programming concepts can add value to object-oriented languages. =) – Erik Forbes Jan 15 '10 at 20:32