访问者模式是一种设计模式,它允许一个或者多个操作应用到一组对象上,解耦操作和对象本身。在.NET 3.5中,实现访问者模式并不简单,因为需要基于静态类型来选择调用的Visit方法。但是,随着.NET 4的发布,其动态类型的能力使得实现访问者模式变得更加简单和灵活。
在本文中,将探讨如何在.NET 4中实现访问者模式,包括如何使用动态类型和扩展方法。假设读者已经了解双分派访问者模式的基本概念。
访问者模式的核心代码如下:
public static void Accept(this object objItem, Visitor objVisitor)
{
try
{
((dynamic)objVisitor).Visit((dynamic)objItem);
}
catch (RuntimeBinderException excException)
{
if (objVisitor.Rethrow)
throw;
objVisitor.Fallback(objItem, excException);
}
}
通过将objItem和objVisitor声明为动态类型,可以强制执行多态的Visit方法调用。这将选择完全匹配的方法或与objItem最接近的基类匹配的方法。
Visitor类的基本结构如下:
public abstract class Visitor
{
public bool Rethrow { get; private set; }
protected Visitor(bool bRethrow) { Rethrow = bRethrow; }
public abstract void Fallback(object objItem, Exception excException);
}
用户定义的Visitor需要定义所需的Visit方法,例如:
public class BaseVisitor : Visitor
{
public BaseVisitor() : base(false) { }
public override void Fallback(object objItem, Exception excException)
{
Console.WriteLine("{0}.Fallback({1}, {2})", this.GetType().Name, objItem.GetType().Name, excException.GetType().Name);
}
public virtual void Visit(Base objItem)
{
Console.WriteLine("BaseVisitor.Visit({0})", objItem.GetType().Name);
}
public virtual void Visit(Derived_A objItem)
{
Console.WriteLine("BaseVisitor.Visit({0})", objItem.GetType().Name);
}
}
特定的Visitor可能如下所示:
public class SpecificVisitor : BaseVisitor
{
public void Visit(Container objItem)
{
Console.WriteLine("SpecificVisitor.Visit({0})", objItem.GetType().Name);
foreach (var objElement in objItem.Elements)
{
objElement.Accept(this);
}
}
}
访问包含多态数据的容器将按预期工作:
class Program
{
static void Main(string[] args)
{
Container c = new Container();
c.Elements.Add(new Base());
c.Elements.Add(new Derived_A());
c.Elements.Add(new Derived_B());
c.Elements.Add(new Derived_A_A());
BaseVisitor objVisitor = new SpecificVisitor();
c.Accept(objVisitor);
}
}
测试类可能如下所示:
public class Base {}
public class Derived_A : Base {}
public class Derived_B : Base {}
public class Derived_A_A : Derived_A {}
public class Container
{
public IList Elements { get; set; }
public Container() { Elements = new List(); }
}