【问题标题】:Generic Interface For Hierarchical Collection分层集合的通用接口
【发布时间】:2011-04-02 18:00:43
【问题描述】:

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

(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 - 我不需要/想要此类中的 children 属性,但是,如果可以正确构建整个层次结构,我愿意在类中挂起一个空集合。

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

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

克里斯

【问题讨论】:

标签: c# linq generics interface lambda


【解决方案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&lt;C&gt; 限制在具体类型而不是接口上。这取决于您是想通过接口还是通过具体类来引用对象。

让我知道这是否适合你。

【讨论】:

    【解决方案2】:

    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);  
        }
    }
    

    超类(无论是抽象的还是具体的)的工作方式与接口非常相似,但具有可以从它们实例化对象的优点,请参阅代码工作。

    在我看来,您正在尝试做一些非常概念化的事情。也许如果您退后一步,转到可以实例化的类,您可能会更实际地了解您的想法。

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

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

      • 我同意你的方法,因为它很简单,但是,我想要一个包含子属性的属性,而不是两个属性。
      • 和@Chris。它认为在其中一种情况下,一个类应该具有受保护的读写属性,以及相同值的公共函数或只读属性
      • 暴露List&lt;T&gt; 类型的东西通常是不好的,因为它会改变例如Item 来自将结果封装成封装实体的东西;您的班级将无法知道您的结果中可能存在哪些其他引用,或者其他代码可能对它们做什么。最好是返回IList&lt;Result&gt; 和/或定义一个FolderFolders 类,该类实现IList&lt;Folder&gt;,封装一个字段Folder parent,并将this[index] 链接到parent.GetFolder(index) [同样为FolderItems 定义类和FolderResults].
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-01-27
      • 2012-03-07
      • 1970-01-01
      • 2014-10-17
      • 2011-07-12
      • 2013-01-23
      • 2015-08-18
      相关资源
      最近更新 更多