65.9K
CodeProject 正在变化。 阅读更多。
Home

C# 中的多态扩展访问者

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.45/5 (7投票s)

2010年9月26日

CPOL

1分钟阅读

viewsIcon

37421

downloadIcon

192

.NET 4 最终允许定义多态扩展访问者

引言

我喜欢(双分派)访问者模式来遍历对象树。但有些代码没有提供访问者接口。因此,我已经在 .NET 3.5 中寻找一种简单而强大的方法来实现扩展的访问者模式。这失败了,因为调用的 Visit 方法是基于 static 类型而不是动态类型选择的。

现在 .NET 4 框架及其动态类型功能解决了这个问题。

在本文中,我假设读者了解双分派访问者模式(例如,参见 http://en.wikipedia.org/wiki/Visitor_pattern)。

Using the Code

此访问者模式的核心是以下代码

// General Accept extension method.
public static void Accept(this object objItem, Visitor objVisitor)
{
    try
    {
        ((dynamic)objVisitor).Visit((dynamic)objItem); // polymorphic Visit call
    }
    catch (RuntimeBinderException excException)
    {
        if (objVisitor.Rethrow) throw;
        objVisitor.Fallback(objItem, excException);
    }
}   

objItem 设为动态对象,可以强制多态地调用最匹配的 Visit 方法。它采用完全匹配的方法,或者匹配 objItem 最接近基类的方法。

objVisitor 设为动态对象,允许定义具有在访问者类层次结构的任何级别上定义的任何附加 Visit 方法的多态访问者类层次结构。无需在所有类中添加新的 Visit 方法;您也可以仅在叶类中添加额外的 Visit

相应的根 Visitor 类如下所示

// extension visitor basics
public abstract class Visitor
{
    // controls if a missing Visit method throws an exception (true) 
    // or if it executes the Fallback method (false).
    public bool Rethrow { get; private set; }
    // bRethrow = true: rethrow if no matching visit method found, false: 
    // call Fallback method instead
    protected Visitor(bool bRethrow)
    {
        Rethrow = bRethrow;
    }
    // fall back method that is called if no matching visit method is found
    public abstract void Fallback(object objItem, Exception excException);
}

用户定义的 Visitor 定义所需的 Visit 方法,例如:

// base visitor
public class BaseVisitor: VisitorExtension.Visitor
{
    #region construction and basic framework
    // The visitor calls Fallback if no matching Visit method is found.
    public BaseVisitor()
        : base(false)
    {
    }
    // generic fall back if no visit method is found
    public override void Fallback(object objItem, Exception excException)
    {
        Console.WriteLine("{0}.Fallback({1}, {2})", 
	this.GetType().Name, objItem.GetType().Name, excException.GetType().Name);
    }
    #endregion
    #region User defined Visit methods
    // visit method implementations
    public virtual void Visit(Base objItem)
    {
        Console.WriteLine("BaseVisitor.Visit<Base>({0})", objItem.GetType().Name);
    }
    public virtual void Visit(Derived_A objItem)
    {
        Console.WriteLine("BaseVisitor.Visit<Derived_A>({0})", objItem.GetType().Name);
    }
    #endregion
}
// Specific visitor
public class SpecificVisitor : BaseVisitor
{
    // visit all elements and call the Extension method Accept 
    // which calls polymorphically the appropriate Visit method.
    virtual public void Visit(Container objItem)
    {
        Console.WriteLine("SpecificVisitor.Visit<Container>({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());
        // SpecificVisitor provides the following Visit methods
        // - Visit(Base)
        // - Visit(Derived_A)
        // - Visit(Container)
        BaseVisitor objVisitor = new SpecificVisitor();
        c.Accept(objVisitor);     // visits polymorphically the elements
        12345.Accept(objVisitor); // triggers Fallback
    }
}
// test classes
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<Base> Elements { get; set; }
    public Container()
    {
        Elements = new List<Base>();
    }
}

Sample Output

关注点

我很高兴 .NET 4 终于允许这样做——在 .NET 3.5 中,我非常沮丧,因为我无法以一种表达的方式做到这一点。

历史

  • 2010-09-26:初始版本
  • 2010-09-27:改进了代码格式
© . All rights reserved.