【问题标题】:Efficient, Immutable, Extensible Collections for .NET [duplicate].NET 的高效、不可变、可扩展集合 [重复]
【发布时间】:2023-04-04 20:10:01
【问题描述】:

在我看来,.NET 极度缺乏安全、不可变的集合类型,尤其是 BCL,但我也没有看到在外部完成太多工作。有没有人有任何指向(最好)生产质量、快速、不可变的 .NET 集合库的指针。快速列表类型是必不可少的。我还没有准备好切换到 F#。

*编辑:搜索者请注意,这很快就会被纳入 BCL:.NET immutable collections

【问题讨论】:

    标签: c# immutability base-class-library


    【解决方案1】:

    【讨论】:

      【解决方案2】:

      .NET BCL 团队为 .NET 4.5 发布了Immutable Collections preview

      【讨论】:

        【解决方案3】:

        【讨论】:

        • 这更像!您知道这些集合在性能方面如何与 Rich 上面提到的 Microsoft.FSharp.Collections 相提并论吗?感谢您的参考,毛里西奥!
        • 这个由 Alexey 设计的库看起来非常精良且干净。我非常感谢你! :-)
        【解决方案4】:

        你可以试试 JaredPar 的 BclExtras

        【讨论】:

          【解决方案5】:

          前段时间我写了一个ImmutableList<T> 课程:

          using System;
          using System.Collections;
          using System.Collections.Generic;
          using System.Linq;
          
          public class ImmutableList<T> : IList<T>, IEquatable<ImmutableList<T>>
          {
              #region Private data
          
              private readonly IList<T> _items;
              private readonly int _hashCode;
          
              #endregion
          
              #region Constructor
          
              public ImmutableList(IEnumerable<T> items)
              {
                  _items = items.ToArray();
                  _hashCode = ComputeHash();
              }
          
              #endregion
          
              #region Public members
          
              public ImmutableList<T> Add(T item)
              {
                  return this
                          .Append(item)
                          .AsImmutable();
              }
          
              public ImmutableList<T> Remove(T item)
              {
                  return this
                          .SkipFirst(it => object.Equals(it, item))
                          .AsImmutable();
              }
          
              public ImmutableList<T> Insert(int index, T item)
              {
                  return this
                          .InsertAt(index, item)
                          .AsImmutable();
              }
          
              public ImmutableList<T> RemoveAt(int index)
              {
                  return this
                          .SkipAt(index)
                          .AsImmutable();
              }
          
              public ImmutableList<T> Replace(int index, T item)
              {
                  return this
                          .ReplaceAt(index, item)
                          .AsImmutable();
              }
          
              #endregion
          
              #region Interface implementations
          
              public int IndexOf(T item)
              {
                  if (_items == null)
                      return -1;
          
                  return _items.IndexOf(item);
              }
          
              public bool Contains(T item)
              {
                  if (_items == null)
                      return false;
          
                  return _items.Contains(item);
              }
          
              public void CopyTo(T[] array, int arrayIndex)
              {
                  if (_items == null)
                      return;
          
                  _items.CopyTo(array, arrayIndex);
              }
          
              public int Count
              {
                  get
                  {
                      if (_items == null)
                          return 0;
          
                      return _items.Count;
                  }
              }
          
              public IEnumerator<T> GetEnumerator()
              {
                  if (_items == null)
                      return Enumerable.Empty<T>().GetEnumerator();
          
                  return _items.GetEnumerator();
              }
          
              public bool Equals(ImmutableList<T> other)
              {
                  if (other == null || this._hashCode != other._hashCode)
                      return false;
                  return this.SequenceEqual(other);
              }
          
              #endregion
          
              #region Explicit interface implementations
          
              void IList<T>.Insert(int index, T item)
              {
                  throw new InvalidOperationException();
              }
          
              void IList<T>.RemoveAt(int index)
              {
                  throw new InvalidOperationException();
              }
          
              T IList<T>.this[int index]
              {
                  get
                  {
                      if (_items == null)
                          throw new IndexOutOfRangeException();
          
                      return _items[index];
                  }
                  set
                  {
                      throw new InvalidOperationException();
                  }
              }
          
              void ICollection<T>.Add(T item)
              {
                  throw new InvalidOperationException();
              }
          
              void ICollection<T>.Clear()
              {
                  throw new InvalidOperationException();
              }
          
              bool ICollection<T>.IsReadOnly
              {
                  get { return true; }
              }
          
              bool ICollection<T>.Remove(T item)
              {
                  throw new InvalidOperationException();
              }
          
              IEnumerator IEnumerable.GetEnumerator()
              {
                  return this.GetEnumerator();
              }
          
              #endregion
          
              #region Overrides
          
              public override bool Equals(object obj)
              {
                  if (obj is ImmutableList<T>)
                  {
                      var other = (ImmutableList<T>)obj;
                      return this.Equals(other);
                  }
                  return false;
              }
          
              public override int GetHashCode()
              {
                  return _hashCode;
              }
          
              #endregion
          
              #region Private methods
          
              private int ComputeHash()
              {
                  if (_items == null)
                      return 0;
          
                  return _items
                      .Aggregate(
                          983,
                          (hash, item) =>
                              item != null
                                  ? 457 * hash ^ item.GetHashCode()
                                  : hash);
              }
          
              #endregion
          }
          

          所有修改集合的方法都返回一个修改后的副本。为了实现IList&lt;T&gt; 接口契约,标准的 Add/Remove/Delete/Clear 方法被显式实现,但它们抛出一个InvalidOperationException

          这个类使用了一些非标准的扩展方法,这里是:

          public static class ExtensionMethods
          {
              public static IEnumerable<T> Append<T>(this IEnumerable<T> source, T item)
              {
                  return source.Concat(new[] { item });
              }
          
              public static IEnumerable<T> SkipFirst<T>(this IEnumerable<T> source, Func<T, bool> predicate)
              {
                  bool skipped = false;
                  foreach (var item in source)
                  {
                      if (!skipped && predicate(item))
                      {
                          skipped = true;
                          continue;
                      }
          
                      yield return item;
                  }
              }
          
              public static IEnumerable<T> SkipAt<T>(this IEnumerable<T> source, int index)
              {
                  return source.Where((it, i) => i != index);
              }
          
              public static IEnumerable<T> InsertAt<T>(this IEnumerable<T> source, int index, T item)
              {
                  int i = 0;
                  foreach (var it in source)
                  {
                      if (i++ == index)
                          yield return item;
          
                      yield return it;
                  }
              }
          
              public static IEnumerable<T> ReplaceAt<T>(this IEnumerable<T> source, int index, T item)
              {
                  return source.Select((it, i) => i == index ? item : it);
              }
          }
          

          这里有一个帮助类来创建ImmutableList&lt;T&gt; 的实例:

          public static class ImmutableList
          {
              public static ImmutableList<T> CreateFrom<T>(IEnumerable<T> source)
              {
                  return new ImmutableList<T>(source);
              }
          
              public static ImmutableList<T> Create<T>(params T[] items)
              {
                  return new ImmutableList<T>(items);
              }
          
              public static ImmutableList<T> AsImmutable<T>(this IEnumerable<T> source)
              {
                  return new ImmutableList<T>(source);
              }
          }
          

          这是一个用法示例:

              [Test]
              public void Test_ImmutableList()
              {
                  var expected = ImmutableList.Create("zoo", "bar", "foo");
                  var input = ImmutableList.Create("foo", "bar", "baz");
                  var inputSave = input.AsImmutable();
                  var actual = input
                          .Add("foo")
                          .RemoveAt(0)
                          .Replace(0, "zoo")
                          .Insert(1, "bar")
                          .Remove("baz");
          
                  Assert.AreEqual(inputSave, input, "Input collection was modified");
                  Assert.AreEqual(expected, actual);
              }
          

          我不能说它的生产质量,因为我没有彻底测试它,但到目前为止它似乎工作得很好......

          【讨论】:

          • 非常好的实现!但是,如果您直接创建目标数组并使用Array.Copy,在压力下可能会更快。 IEnumerable&lt;T&gt; 非常适合可读的函数式代码,但老实说,它会很慢。
          • @Timwi,确实,这个实现当然可以优化。我写的时候不需要一个非常高效的实现,所以我用 Linq 的方式来做,因为它更有趣;)
          • @Timwi - 这种方法至少对内存更友好,因为列表的内容正在被重用,尽管正如你所说的效率低下 - 特别是对于元素的索引访问。类似于使用不可变 cons 单元 (en.wikipedia.org/wiki/Cons) 的 Lisp 列表的东西将提供一个很好的中间立场 - 可以优化对顺序成员的索引访问(获取下一项的固定成本),其中可枚举的解决方案具有可变成本,而避免在列表尾部不变的情况下复制列表的全部内容。
          • @Bittercoder:不,这个实现会复制不可变列表的每个新版本(通过在构造函数中调用ToArray)。
          【解决方案6】:

          您可能想查看FSharp.Core 程序集中的Microsoft.FSharp.Collections 命名空间。您不必在 F# 中编程即可使用这些类型。

          请记住,在 F# 外部使用时,名称会有所不同。例如,F# 中的 Map 在 C# 中称为 FSharpMap

          【讨论】:

          • 嗨 Rich,我正在尝试这些。谢谢。
          • Rich,我认为这些实际上可以在 C# 中实际工作,并为诸如 consing 等典型操作添加一些扩展方法。
          • 不幸的是,所有 F# 集合都是作为类而不是接口实现的。这使得它们不太适合 API 规范。
          • @drozzy 我没有尝试过,但the documentation 建议他们确实实现了所有常用接口。
          【解决方案7】:

          C5 浮现在脑海中,但我不确定它有多快。它已经存在多年,并且非常稳定。

          此外,List&lt;T&gt;.AsReadOnly() 在 IMO 方面做得相当好,但不幸的是,没有字典或任意 ICollection&lt;T&gt; 的等价物。

          【讨论】:

          • 不幸的是,.AsReadOnly() 只返回原始集合的包装器;原始引用仍然是可变的,因此只读包装器也是如此。 C5似乎也是如此。
          • @Timwi 如果您担心原始引用可能会被其他代码修改,那么只需复制它:return new List&lt;T&gt;(myList).AsReadOnly()
          • @romkyns 如果总是制作列表的副本,则不需要不可变列表:-)
          • @drozzy .AsReadOnly() 在许多情况下都相当不错,我们没有更好的内置功能:) 此外,您只需要复制一次,之后列表是不可变的并且可以到处传播。
          猜你喜欢
          • 1970-01-01
          • 2015-12-11
          • 1970-01-01
          • 1970-01-01
          • 2014-10-01
          • 2014-11-23
          • 1970-01-01
          • 1970-01-01
          • 2017-10-23
          相关资源
          最近更新 更多