Ну, ни List<T>
, ни массивы не являются неизменяемыми, поэтому они отсутствуют, если вы действительно после неизменности - вызывающая сторона может привести результат и затем изменить его.
Вы можете создать List<T>
и заверните это в ReadOnlyCollection<T>
.Если ничто больше не имеет ссылки на исходный список, то он фактически неизменен, за исключением отражения.
Если вы на самом деле не заботитесь об неизменности, то есть если вы доверяете всему коду, чтобы не связываться с ним, - тогдаМассив будет наиболее производительным подходом, почти наверняка.Существуют различные оптимизации уровня CLR, благодаря которым они работают невероятно быстро.Однако в этом случае я не приведу к IEnumerable<T>
- я бы просто выставил его как массив.Это ускорит итерацию, чем если бы компилятору пришлось вызывать GetEnumerator()
.
Если компилятор C # видит оператор foreach
в массиве, он генерирует вызовы, чтобы перейти прямо к индексатору и использоватьсвойство Length
... и затем CLR также сможет удалить проверку границ, обнаружив образец.
Аналогичным образом, если вы решите использовать List<T>
, оставьте его как List<T>
-таким образом, вы можете использовать List<T>.Enumerator
- который является структурой - напрямую, без упаковки.
РЕДАКТИРОВАТЬ: Стив Мегсон выдвигает идею использования LINQ для этого.На самом деле, вы, вероятно, можете добиться большего, чем это, потому что, получив перечислитель базового списка, вы можете безопасно передать , что , вызывающей стороне, по крайней мере, для всех коллекций, о которых я знаю.Таким образом, вы можете получить:
public class ProtectedEnumerable<T> : IEnumerable<T>
{
private readonly IEnumerable<T> collection;
public ProtectedEnumerable(IEnumerable<T> collection)
{
this.collection = collection;
}
public IEnumerator<T> GetEnumerator()
{
return collection.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Это означает, что при итерации будет только крошечный удар - только один делегированный вызов GetEnumerator()
.Сравните это с использованием Enumerable.Select
, что потребует дополнительного нажатия на каждый вызов MoveNext()
(а также проекцию no-op).