用于分层收集的通用接口

时间:2011-04-02 18:00:43

标签: c# linq generics interface lambda

我想创建一个紧密遵循以下结构的层次结构:

(1)INodeBase - 包含一个Children属性,它是一个自身的集合(INodeBase)

(2)INodeFolder - 派生自INodeBase。

(3)NodeFolder - 实现INodeFolder。 Children属性的实现应该允许它包含INodeFolder类型和INodeItem类型的项目。

(4)INodeItem - 从INodeBase派生。

(5)NodeItem - 实现INodeItem。 Children属性的实现应该允许它包含NodeResult类型的项目。

(6)INodeResult - 派生自INodeBase。

(7)NodeResult - 实现INodeResult - 我不需要/想要这个类中的子属性,但是,如果可以正确地构造整体层次结构,我愿意在类中挂起一个空集合。 / p>

本质上,我想要节点的一种基本类型 - 并且假设节点可以包含其他节点。我认为泛型是要走的路 - 但问题是,如果我查询整个层次集合,或者更确切地说,如果我过滤它,当我递归遍历每个孩子的子集合(即展平我的我想知道什么是什么 - 也就是说,如果我在一个文件夹的Children集合中,我想知道什么时候我点击了FolderNode并且当我点击一个ItemNode时,然后当我通过Children属性一个ItemNode,我想知道我只是处理NodeResult类型。

那么,上述最佳方法是什么?

克里斯

3 个答案:

答案 0 :(得分:1)

Altought,不是主要答案,为什么不直接使用类和对象而不是接口来设计对象层次结构。稍后您可以将其转换为界面层次结构。

enum NodeTypes { Unknown, Folder, File };

public abstract class CNodeBase {

    protected List<CNodeBase> FItems;

    public List<CNodeBase> Items();

    public virtual NodeTypes NodeType() {
        return NodeTypes.Unknown;
    }
}

public class CNodeFolder: CNodeBase {

// ...

    public override NodeTypes NodeType() {
        return NodeTypes.Folder;
    }

}

public class CNodeFile: CNodeBase {

// ...

    public override NodeTypes NodeType() {
        return NodeTypes.File;
    }

}

public class CDemo {

    void main () {
        // root node is ALWAYS A folder
        CNodeFolder RootNode = new CNodeFolder("\\");

        CNodeBase AnyNode = null;

        AnyNode = new CNodeFolder("c:");
        RootNode.Add(AnyNode);

        AnyNode = new CNodeFolder("d:");
        RootNode.Add(AnyNode);

        AnyNode = new CNodeFile("readme.txt");
        RootNode.Add(AnyNode);  
    }
}

超类(抽象或具体)的工作方式与接口非常相似,但其优点是可以从中实例化对象,看看代码是否正常工作。

在我看来,你正试图做一些非常概念性的事情。也许如果你退后一步,去可以实例化的课程,你可以更实际地看到你的想法。

接口是一个非常好的功能,但起初很难理解。有时候,“退后一步,前进五步”是一个好主意。

答案 1 :(得分:1)

我认为以下代码都遵循您想要的规则,尤其是所有节点实施INodeBase以及NodeResult没有Children集合的奖励。

以下是所需的接口:

public interface INodeBase
{
}

public interface INodeContainer : INodeBase
{
}

public interface INodeContainer<C> : INodeContainer
    where C : INodeBase
{
    IList<C> Children { get; }
}

public interface INodeFolder : INodeContainer<INodeContainer>
{
}

public interface INodeItem : INodeContainer<INodeResult>
{
}

public interface INodeResult : INodeBase
{
}

我添加了几个INodeContainer接口 - 一个非通用接口和一个接口另一种通用。

以下是类定义:

public abstract class NodeBase : INodeBase
{
}

public abstract class NodeBase : INodeBase
{
}

public abstract class NodeContainer : NodeBase, INodeContainer
{
}

public abstract class NodeContainer<C> : NodeContainer, INodeContainer<C>
    where C : INodeBase
{
    public NodeContainer() { this.Children = new List<C>(); }
    public IList<C> Children { get; private set; }
}

public class NodeFolder : NodeContainer<INodeContainer>, INodeFolder
{
}

public class NodeItem : NodeContainer<INodeResult>, INodeItem
{
}

public class NodeResult : INodeResult
{
}

现在你可以使用这样的代码:

var nf = new NodeFolder();
nf.Children.Add(new NodeFolder()); // Add `INodeFolder`
var ni = new NodeItem();
nf.Children.Add(ni);  // or add `INodeFolder`, but nothing else.
var nr = new NodeResult();
ni.Children.Add(nr); // Only add `INodeResult`.
// nr does not have "Children" collection.

如果您愿意,可以在具体类型而不是接口上限制INodeContainer<C>。这取决于您是想通过接口还是具体类来引用对象。

请告诉我这是否适合您。

答案 2 :(得分:0)

为什么需要让文件夹和项目实现相同的界面?

在我看来,这样一个更简单的结构会更好......

public class Folder
{
    public List<Folder> Folders { get; }
    public List<Item> Items { get; }
}

public class Item
{
    public List<Result> Results { get; }
}

public class Result
{

}