使用委托来简化当前使用Visitor模式的C#(2.0)中的类型安全抽象语法树

时间:2009-12-03 16:07:46

标签: c# .net delegates

我有一些“输入”可以通过多种不同的方式进行解析。

我正在尝试从需要解析结果的各种下游引擎中抽象出解析代码(某些部分将显示它,其他部分实际执行它等等。)

到目前为止,我有这个:

interface IParsedNode
{
    // Visits this node, returns whatever the appropriate IVisitor function
    // returned.
    T Visit<T>(IVisitor<T> visitor);
}

interface IVisitor<T>
{
    T Load(Aspect aspect);
    T LoadFile(string group, string filename);
    T Process(PluginProperties pluginProperties, IParsedNode input);
    T Aggregate(List<IParsedNode> children);
    T Rename(string newName, string oldName, IParsedNode input);
    T Filter(string keep, IParsedNode input);
    T Cache(IParsedNode input);
}

class Cached : IParsedNode
{
    private readonly IParsedNode input;

    public Cached(IParsedNode input)
    {
        this.input = input;
    }
    #region IParsedNode Members
    public T Visit<T>(IVisitor<T> visitor)
    {
        return visitor.Cache(input);
    }
    #endregion
}

class Filter : IParsedNode
{
    private readonly string keep;
    private readonly IParsedNode input;

    public Filter(string keep, IParsedNode input)
    {
        this.keep = keep;
        this.input = input;
    }

    #region IParsedNode Members
    public T Visit<T>(IVisitor<T> visitor)
    {
        return visitor.Filter(keep, input);
    }
    #endregion
}

正如你所看到的,这允许我有一个完全“抽象”的解析树,这也是类型安全的。我也喜欢一切都是不可改变的事实。

我抽象出“T”的类型,因为不同的下游系统将依次从抽象的解析树中创建自己的具体图形。

例如,这是IVisitor的一个实现:

// Used to convert the abstract tree into an actual tree that can then be post-processed.
class NodeTreeBuilder : IVisitor<Node>
{
    private readonly NodeFactory nodeFactory;

    public NodeTreeBuilder(NodeFactory nodeFactory)
    {
        this.nodeFactory = nodeFactory;
    }

    #region IVisitor<Node> Members
    public Node Load(Aspect aspect)
    {
        return nodeFactory.CreateRaw(aspect);
    }

    public Node LoadFile(string group, string filename)
    {
        return nodeFactory.CreateFile(group, filename);
    }

    public Node Process(PluginProperties pluginProperties, IParsedNode input)
    {
        ProcessInfo processInfo = new ProcessInfo();
        processInfo.AssemblyPath = pluginProperties.AssemblyPath;
        processInfo.ClassName = pluginProperties.ClassName;
        processInfo.Config = new PluginConfig(pluginProperties.Config, pluginProperties.HashConfig, pluginProperties.DeltaType);
        PluginInfo pluginInfo = Registry.CreatePluginInfo(pluginProperties.Id, processInfo);
        return nodeFactory.CreatePostProcess(pluginInfo, input.Visit(this), pluginProperties.RunOnEmpty);
    }

    public Node Aggregate(List<IParsedNode> children)
    {
        Node[] convertedChildren = children.ConvertAll<Node>(delegate(IParsedNode child) { return child.Visit(this); }).ToArray();
        return nodeFactory.CreateAggregated(convertedChildren);
    }

    public Node Rename(string newName, string oldName, IParsedNode input)
    {
        return nodeFactory.Rename(oldName, newName, input.Visit(this));
    }

    public Node Filter(string keep, IParsedNode input)
    {
        return nodeFactory.Filter(keep, input.Visit(this));
    }

    public Node Cache(IParsedNode input)
    {
        return input.Visit(this).Cache(true);
    }
    #endregion
}

对于IVisitor的实际具体实现,这一切都比我希望的更好。

然而,实现IParsedNode本身(正如你最初看到我做的那样)证明有点单调乏味。我提供了一大堆只包含一个方法的接口实现....这让我想到也许我可以使用委托来减少膨胀:

class ParsedNode : IParsedNode
{
    delegate T NodeType<T>(IVisitor<T> visitor);

    private readonly NodeType nodeType;

    public ParsedNode<T>(NodeType<T> nodeType)
    {
        this.nodeType = NodeType;
    }

    public T  Visit<T>(IVisitor<T> visitor)
    {
        return nodeType(visitor);
    }
}

但上面没有编译。我是否没有办法在某种通用委托方面实现IParsedNode?如果有办法让这项工作变得不那么冗长,那就太好了。

也许如果IParsedNode接口本身只是一个委托,这可能会起作用吗?

1 个答案:

答案 0 :(得分:1)

我建议阅读Judith Bishop(C# 3.0 Design Patterns的作者)题为On the Efficiency of Design Patterns Implemented in C# 3.0的论文。

她使用委托专门处理访问者模式,并介绍基本实现(至少在算法上)。她的实施非常快,非常灵活。

这是我最喜欢的C#中访问者模式的实现,到目前为止。