Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#4.0

Dynamic Visitor Pattern

4.55/5 (18 votes)
3 May 2013CPOL2 min read 54.4K   218  
Visitor design pattern - dynamic implementation.

Introduction   

Visitor “represent[s] an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates” (GoF).

I believe you know the Visitor design pattern, so here is dynamic implementation. The dynamic visitor separates the pattern from the business logic. The visitor pattern is based on double-dispatch, so we should implement runtime dispatching. We could do this one  for example thru dynamic or Type.

Following class hierarchy is used in all examples 

Image 1 

Visitor based on "Dynamic

The dynamic type is resolved at runtime. By specify concrete type to dynamic type we can dispatch to concrete action.   

The code consists of several classes:

  • Visitor - factory for visitor
  • IActionVisitor<in TBase> - visitor's interface 

IActionVisitor<in TBase>  -  lets you register Action<T> on concrete type 

C#
public interface IActionVisitor<in TBase>
        where TBase : class
{
    /// <summary>
    /// Register action on <see cref="T"/>.
    /// </summary>
    /// <typeparam name="T">Concrete type.</typeparam>
    /// <param name="action">Action.</param>
    void Register<T>(Action<T> action)
        where T : TBase;

    /// <summary>
    /// Visit concrete type.
    /// </summary>
    /// <param name="value">Type to visit.</param>
    void Visit<T>(T value)
        where T : TBase;
}  

Visitor - factory for IActionVisitor<in TBase>  visitor.   

C#
public static class Visitor
{
    /// <summary>
    /// Create <see cref="IActionVisitor{TBase}"/>.
    /// </summary>
    /// <typeparam name="TBase">Base type.</typeparam>
    /// <returns>New instance of <see cref="IActionVisitor{TBase}"/>.</returns>
    public static IActionVisitor<TBase> For<TBase>()
        where TBase : class
    {
        return new ActionVisitor<TBase>();
    }

    private sealed class ActionVisitor<TBase> : IActionVisitor<TBase>
        where TBase : class
    {
        private readonly Dictionary<Type, dynamic> _repository =
            new Dictionary<Type, dynamic>();

        public void Visit<T>(T value)
            where T : TBase
        {
            dynamic action = _repository[value.GetType()];
            action((dynamic)value);
        }

        public void Register<T>(Action<T> action)
            where T : TBase
        {
            _repository[typeof(T)] = action;
        }
    }
}  

The magic happens here 

private readonly Dictionary<Type, dynamic> _repository = new Dictionary<Type, dynamic>(); 

and here 

public void Visit<T>(T value)
    where T : TBase
{
    dynamic action = _repository[value.GetType()];
    action((dynamic)value);
}

DLR (dynamic language runtime) resolves type and executes concrete action. 

Example 

C#
private static void DynamicActionVisitor()
{
    Stopwatch stopwatch = Stopwatch.StartNew();

    IActionVisitor<Letter> visitor = Visitor.For<Letter>();
    visitor.Register<A>(x => Console.WriteLine(x.GetType().Name));
    visitor.Register<B>(x => Console.WriteLine(x.GetType().Name));

    Letter a = new A();
    Letter b = new B();
    visitor.Visit(a);
    visitor.Visit(b);

    stopwatch.Stop();
    Console.WriteLine("Execution time: {0} ms", stopwatch.Elapsed.TotalMilliseconds);
}

Here is the result of running  

Image 2 

First call with runtime magic is too expensive, so the Visitor based on dynamic is useful only with many calls. 

Visitor based on "Type

By specify concrete type to Action<T> we can dispatch to concrete action.

The code consists of several classes:

  • Visitor - factory for visitor
  • IActionVisitor<in TBase> - visitor's interface 

IActionVisitor<in TBase>  -  lets you register Action<T> on concrete type 

C#
public interface IActionVisitor<in TBase>
        where TBase : class
{
    /// <summary>
    /// Register action on <see cref="T"/>.
    /// </summary>
    /// <typeparam name="T">Concrete type.</typeparam>
    /// <param name="action">Action.</param>
    void Register<T>(Action<T> action)
        where T : TBase;

    /// <summary>
    /// Visit concrete type.
    /// </summary>
    /// <param name="value">Type to visit.</param>
    void Visit<T>(T value)
        where T : TBase;
}  

Visitor - factory for IActionVisitor<in TBase>  visitor.   

C#
public static class Visitor
{
    /// <summary>
    /// Create <see cref="IActionVisitor{TBase}"/>.
    /// </summary>
    /// <typeparam name="TBase">Base type.</typeparam>
    /// <returns>New instance of <see cref="IActionVisitor{TBase}"/>.</returns>
    public static IActionVisitor<TBase> For<TBase>()
        where TBase : class
    {
        return new ActionVisitor<TBase>();
    }

    private sealed class ActionVisitor<TBase> : IActionVisitor<TBase>
        where TBase : class
    {
        private readonly Dictionary<Type, Action<TBase>> _repository =
            new Dictionary<Type, Action<TBase>>();

        public void Register<T>(Action<T> action)
            where T : TBase
        {
            _repository[typeof(T)] = x => action((T)x);
        }

        public void Visit<T>(T value)
            where T : TBase

        {
            Action<TBase> action = _repository[value.GetType()];
            action(value);
        }
    }
}   

The magic is here, we create new Action<TBase> and do not lose the concrete type 

public void Register<T>(Action<T> action)
    where T : TBase
{
    _repository[typeof(T)] = x => action((T)x);
}

and here.  We use value.GetType()  for resolving object type in runtime 

 public void Visit<T>(T value)
    where T : TBase

{
    Action<TBase> action = _repository[value.GetType()];
    action(value);
}

Example 

C#
private static void DynamicActionVisitor()
{
    Stopwatch stopwatch = Stopwatch.StartNew();

    IActionVisitor<Letter> visitor = Visitor.For<Letter>();
    visitor.Register<A>(x => Console.WriteLine(x.GetType().Name));
    visitor.Register<B>(x => Console.WriteLine(x.GetType().Name));

    Letter a = new A();
    Letter b = new B();
    visitor.Visit(a);
    visitor.Visit(b);

    stopwatch.Stop();
    Console.WriteLine("Execution time: {0} ms", stopwatch.Elapsed.TotalMilliseconds);
}

Here is the result of running

Image 3 

Visitor based on "Type" - Full version  

IActionVisitor<in TBase>, IFuncVisitor<in TBase, TResult>   -  lets you register Action<T>, Func<T, TResult> on concrete type  

public interface IFuncVisitor<in TBase, TResult>
    where TBase : class
{
    /// <summary>
    /// Register action on <see cref="T"/>.
    /// </summary>
    /// <typeparam name="T">Concrete type.</typeparam>
    /// <param name="action">Action.</param>
    void Register<T>(Func<T, TResult> action)
        where T : TBase;

    /// <summary>
    /// Visit concrete type.
    /// </summary>
    /// <param name="value">Type to visit.</param>
    /// <returns>Result value.</returns>
    TResult Visit<T>(T value)
        where T : TBase;
}
public interface IActionVisitor<in TBase>
    where TBase : class
{
    /// <summary>
    /// Register action on <see cref="T"/>.
    /// </summary>
    /// <typeparam name="T">Concrete type.</typeparam>
    /// <param name="action">Action.</param>
    void Register<T>(Action<T> action)
        where T : TBase;

    /// <summary>
    /// Visit concrete type.
    /// </summary>
    /// <param name="value">Type to visit.</param>
    void Visit<T>(T value)
        where T : TBase;
}

Visitor - factory for IFuncVisitor<in TBase, TResult>, IActionVisitor<in TBase>  visitors. 

public static class Visitor
{
    public static IFuncVisitor<T, TResult> For<T, TResult>()
        where T : class
    {
        return new FuncVisitor<T, TResult>();
    }

    public static IActionVisitor<T> For<T>()
        where T : class
    {
        return new ActionVisitor<T>();
    }

    private sealed class ActionVisitor<TBase> : IActionVisitor<TBase>
        where TBase : class
    {
        private readonly Dictionary<Type, Action<TBase>> _repository =
            new Dictionary<Type, Action<TBase>>();

        public void Register<T>(Action<T> action)
            where T : TBase
        {
            _repository[typeof(T)] = x => action((T)x);
        }

        public void Visit<T>(T value)
            where T : TBase
        {
            Action<TBase> action = _repository[value.GetType()];
            action(value);
        }
    }

    private sealed class FuncVisitor<TBase, TResult> : IFuncVisitor<TBase, TResult>
        where TBase : class
    {
        private readonly Dictionary<Type, Func<TBase, TResult>> _repository =
            new Dictionary<Type, Func<TBase, TResult>>();

        public void Register<T>(Func<T, TResult> action)
            where T : TBase
        {
            _repository[typeof(T)] = x => action((T)x);
        }

        public TResult Visit<T>(T value)
            where T : TBase
        {
            Func<TBase, TResult> action = _repository[value.GetType()];
            return action(value);
        }
    }
}

Conclusion 

That's all for now, I hope you enjoyed it, please take the time to post a comment. Thanks for reading the article. 

History 

  • 17th March, 2013: Initial version.
  • 03d  May, 2013: Added Visitor based on Type 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)