.NET、特にBCLの安全で不変のコレクションタイプが極端に不足しているように見えますが、外部でも多くの作業が行われているのを見たことがありません。.NET用の(できれば)本番品質の高速で不変のコレクションライブラリへのポインタを持っている人はいますか?ファストリストタイプは不可欠です。F#に切り替える準備はまだできていません。
*編集:検索者への注意、これはまもなくBCLに組み込まれます: .NET不変コレクション
.NET、特にBCLの安全で不変のコレクションタイプが極端に不足しているように見えますが、外部でも多くの作業が行われているのを見たことがありません。.NET用の(できれば)本番品質の高速で不変のコレクションライブラリへのポインタを持っている人はいますか?ファストリストタイプは不可欠です。F#に切り替える準備はまだできていません。
*編集:検索者への注意、これはまもなくBCLに組み込まれます: .NET不変コレクション
アセンブリMicrosoft.FSharp.Collections
内の名前空間を確認することをお勧めします。これらのタイプを利用するためにF#でプログラムする必要はありFSharp.Core
ません。
F#の外部から使用すると、名前が異なることに注意してください。たとえば、F#のinはC#からMap
として知られています。FSharpMap
.NET BCLチームは、.NET4.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<T>
インターフェイスコントラクトを満たすために、標準の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<T>
:
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);
}
徹底的にテストしていないので、生産品質とは言えませんが、今のところ問題なく動作しているようです...
C5が頭に浮かびますが、どれくらい速いかはわかりません。それは何年も前からあり、非常に安定しています。
さらに、List<T>.AsReadOnly()
IMOの仕事はかなりうまくいきますが、残念ながら、辞書や任意ICollection<T>
の辞書に相当するものはありません。
ExtrasまたはSystem.collections.concurrent チュートリアルをご覧ください。
JaredParのBclExtrasを試すことができます。