【问题标题】:Implementing a dynamic FlatCollection<T>实现动态 FlatCollection<T>
【发布时间】:2014-03-28 20:41:17
【问题描述】:

我需要创建或使用现有的 .NET FW FlatCollection。 我所说的 FlatCollection 是一个集合,当通过 Add 方法/函数插入一个元素时,它将检查该元素,如果该元素是 IEnumerable,则将从该 IEnumerable 中获取所有元素并将它们添加到收藏。 (此行为仅适用于根 IEnumerable,但是,将其递归地应用于子 IEnumerable 也可能很好,尽管更难)。诀窍是,这个 FlatCollection 作为一个索引集合,需要保持其元素的顺序。

假设用户向集合中添加了一个由 3 个元素组成的 IEnumerable,然后在某个时间点向被引用的集合中添加了一个新元素; FlatCollection 需要注意列表的大小发生了变化,并更新其内部集合以反映新元素,保持排序。因此,例如,如果用户在两个单元素之间添加了一个 Empty 集合,然后用 3 个元素填充该集合,则这两个单元素仍将位于新添加的 3 个元素之前和之后。

当然,我可以通过从集合继承并定义所有这些行为来自己实现这一点,但这很棘手,因为您需要对添加的 IEnumerable 中的项目数进行计数(IEnumerable 没有计数),以及它在集合中的起始索引。想到的第一件事是使用链表作为内部集合,因为您会调整很多大小,但我不确定什么是最好的。

但是,如果有一种更简单的方法可以做到这一点,而不是从头开始实施整个事情。使用我想知道的现有 .NET FrameWork 元素!因为这是我的问题的目的。

总结一下: 我正在寻找实现 FlatCollection 的最佳性能和最简单的方法,可能使用 .NET FW 元素。

【问题讨论】:

  • 需要实现哪些接口?如果您只需要IEnumerable&lt;T&gt;,我会保留原始 集合,然后懒惰地迭代它们。
  • @JonSkeet 这是我为 Windows Phone 8 编写的自定义 CompositeCollection 实现。因此,ItemsControl 的 ItemsSource 调用任何接口方法来填充此类 ItemsControl 的项目都是我需要的。
  • @JonSkeet 起初我以为它只使用了枚举器,因为 ItemsSource 是 IEnumerable 类型,但是,在编写了一个以这种方式枚举惰性集合的 CompositeEnumerator (1drv.ms/P9mzRx) 之后,我注意到它不仅仅使用 IEnumerable 中的方法。 .NET CompositeCollection 类使用 IList、ICollection、IEnumerable、INotifyCollectionChanged,所以我需要使用这些的通用版本。
  • @FranciscoAguilera 您真的应该将其视为树而不是集合。我相信它会让解决这个问题变得更容易。
  • @Euphoric 听起来绝对是个好方法,前提是这个树形结构暴露了必要的界面元素。我不知道你是否看到,但我需要这个 FlatCollection 来实现 IList、ICollection、IEnumerable 和 INotifyCollectionChanged(将更改传播到 UI)。我不确定实施这些是否违反了这种树结构的原则。

标签: c# algorithm data-structures collections


【解决方案1】:

这看起来像是某种树状结构。每个“集合”都是节点,每个节点可以有任意数量的子节点,值是叶子。通过按顺序处理仅叶子,您可以创建所需的“平面”集合。它还可以递归工作,因此您可以询问任何节点,它会为您提供正确的“平面子集合”。

【讨论】:

    【解决方案2】:

    Enumerable.SelectMany 可以满足您的所有需求,如果您的“平面收藏”很简单 List&lt;IEnumerable&lt;T&gt;&gt;

      List<IEnumerable<MyItemType>> listOfEnumerables = new ...
      IEnumerable<List<MyItemType>> flattened =
                    listOfEnumerables.Select(x => x);
    

    如果您想隐藏详细信息,也可以将该调用包装在一个类中:

     class FlatCollection<T> : IEnumerable<T>
     {
        List<IEnumerable<T>> list = new ...
    
        public Add(T item) { list.Add(Enumerable.Repeat(item, 1);}
        public Add(IEnumerable<T> item) { list.Add(item);}
    
        // use Enumerable.SelectMany to imeplent IEnumerable
      }
    

    【讨论】:

    • 您不能设置此集合中的第一项,因为IEnumerable 不提供任何突变操作。
    • 这会支持以前添加的 IEnumerables 的索引扩展吗?如果用户更改了之前添加的 IEnumerable,更改是否会传播到 FlatCollection
    • @Servy 不知道你的意思是“你不能设置第一个项目”......我不认为集合本身需要将新元素添加到现有的 IEnumerable 中(如 listOfEnumerables [ 3].Add(newItem)`).
    • @FranciscoAguilera - 是的......如果你添加真正的可枚举而不是副本 - 即添加 myEnumerable.ToList() 将不起作用......
    • @AlexeiLevenkov Enumerable.Repeat(item, 1) 返回一个 IEnumerable。我不明白将这个枚举中的所有项目添加到列表中如何支持我所说的索引扩展,你能澄清一下吗? (我还假设您打算使用 list.AddRange 而不是 list.Add )谢谢!
    猜你喜欢
    • 1970-01-01
    • 2020-07-19
    • 2011-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-17
    • 1970-01-01
    • 2015-04-22
    相关资源
    最近更新 更多