Вам, вероятно, известна следующая проблема: вы хотите разделить коллекцию между двумя объектами A и 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 обеспечивают разумное решение проблемы, но также страдают от некоторых проблем: .ToList () копирует весь список - так что в итоге я получаю x экземпляров одной коллекции, в то время как ReadOnlyCollection гарантирует, что клиенты не изменят коллекцию, но не дадут клиентам гарантии того, что коллекция не изменится.
На самом деле, с такой ситуацией я часто сталкиваюсь, потому что большинство классов, которые я пишу (предположительно: 80-90%), являются эффективно неизменяемыми и часто собирают коллекции, которые в основном представляют неизменяемые массивы (что означает в данном контексте: фиксированный размер неизменяемой последовательности предметов). С моей точки зрения, хорошее решение этой проблемы довольно простое: создайте класс-оболочку для массива, который гарантированно будет правильно инициализирован в конструкторе, и предоставьте только методы / свойства, которые не изменяют базовый массив.
Короче говоря: есть ли что-то не так со следующей (тривиальной) реализацией концепции неизменяемого массива?
Кстати: я решил, что Sequence - подходящее имя, так как
а) он достаточно хорошо описывает намерение (неизменяемая последовательность предметов фиксированного размера)
б) это не имя, используемое в настоящее время .net и
в) он очень короткий, что важно, потому что я планирую широко его использовать.
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);
}
}
}