おそらく次の問題をご存知でしょう: 2 つのオブジェクト A と B の間でコレクションを共有したい (または、同様にプロパティによってコレクションを公開したい) と ... まあ、これは実際には良い考えではないことに気付きます。そしてBはコレクションを変更し、何とかハルマゲドンを手に入れることができます...
しかし、コレクション全体を共有するのではなく、コレクションのアイテムを使用してオブジェクトを初期化するか、アイテムをオブジェクトのメソッドに渡したいだけであることに気付くことがよくあります。さらに、オブジェクトのアイテムはオブジェクトの存続期間中に変更されないため、多くの場合、次のようなことを行うことになります。
public class Foo
{
private List<int> items;
public Foo(IEnumerable<int> items)
{
this.items = items.ToList();
}
public ReadOnlyCollection<int> Items
{
get { return new ReadOnlyCollection(this.items); }
}
}
.ToList() と ReadonlyCollection-Wrapper の両方が問題に対する合理的な解決策を提供しますが、いくつかの問題もあります。クライアントはコレクションを変更しませんが、コレクションが変更されないという保証をクライアントに与えません。
実際、私が書くほとんどのクラス (推測: 80 - 90 %) は事実上不変であり、基本的に不変配列を表すコレクションを集約するため、これは私がよく直面する状況です (このコンテキストでは、固定サイズの不変シーケンスを意味します)。アイテムの)。私の見解では、この問題に対する適切な解決策は非常に単純です。コンストラクターで適切に初期化されることが保証されている配列のラッパー クラスを構築し、基になる配列を変更しないメソッド/プロパティのみを提供します。
簡単に言えば、不変配列の概念の次の(自明な)実装に問題はありますか?
ところで:私はシーケンスが適切な名前であると判断しました.a)それは意図を非常によく説明しています(アイテムの固定サイズの不変シーケンス)b).netで現在使用されている名前ではなく、c)それは非常に短いです私はそれを広範囲に使用することを計画しているので重要です。
public sealed class Sequence<T> : IEnumerable<T>
{
private readonly T[] items;
public Sequence(IEnumerable<T> items)
{
this.items = items.ToArray();
}
public Sequence(int size, Func<int, T> producer)
{
this.items = new T[size];
for (int i = 0; i < size; i++)
{
this.items[i] = producer(i);
}
}
public T this[int i]
{
get { return this.items[i]; }
}
public int Length
{
get { return this.items.Length; }
}
public bool Contains(T item)
{
for (int i = 0; i < this.items.Length; i++)
{
if (this.items[i].Equals(item))
return true;
}
return false;
}
public Enumerator<T> GetEnumerator()
{
return new Enumerator<T>(this);
}
IEnumerator<T> System.Collections.Generic.IEnumerable<T>.GetEnumerator()
{
return GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public struct Enumerator<T> : IEnumerator<T>
{
private T[] items;
private int nextIndex;
private T current;
internal Enumerator(Sequence<T> immutableArray)
{
this.items = immutableArray.items;
this.nextIndex = 0;
this.current = default(T);
}
public T Current
{
get { return this.current; }
}
object System.Collections.IEnumerator.Current
{
get { return this.Current; }
}
public void Dispose()
{
}
public bool MoveNext()
{
if (this.nextIndex < this.items.Length)
{
this.current = this.items[this.nextIndex];
this.nextIndex++;
return true;
}
else
{
return false;
}
}
public void Reset()
{
this.nextIndex = 0;
this.current = default(T);
}
}
}