В моем коде нужно использовать IEnumerable<>
несколько раз, таким образом получаю ошибку Resharper «Возможное многократное перечисление IEnumerable
».
Образец кода:
public List<object> Foo(IEnumerable<object> objects)
{
if (objects == null || !objects.Any())
throw new ArgumentException();
var firstObject = objects.First();
var list = DoSomeThing(firstObject);
var secondList = DoSomeThingElse(objects);
list.AddRange(secondList);
return list;
}
- Я могу изменить
objects
параметр на,List
а затем избежать возможного многократного перечисления, но тогда я не получу самый высокий объект, который смогу обработать. - Другое дело , что я могу сделать , это преобразовать
IEnumerable
вList
начале метода:
public List<object> Foo(IEnumerable<object> objects)
{
var objectList = objects.ToList();
// ...
}
Но это просто неловко .
Что бы вы сделали в этом сценарии?
источник
IReadOnlyCollection(T)
(новый с .net 4.5) лучший интерфейс, чтобы передать идею о том, что онIEnumerable(T)
предназначен для перечисления несколько раз. Как говорится в этом ответе,IEnumerable(T)
сам по себе настолько универсален, что может даже ссылаться на невосстановимый контент, который не может быть перечислен заново без побочных эффектов. НоIReadOnlyCollection(T)
подразумевает повторяемость..ToList()
в начале метода, вы должны сначала проверить, не является ли параметр нулевым. Это означает, что вы получите два места, где вы генерируете ArgumentException: если параметр имеет значение null и если у него нет элементов.IReadOnlyCollection<T>
vs,IEnumerable<T>
а затем опубликовал вопрос об использованииIReadOnlyCollection<T>
в качестве параметра и в каких случаях. Я думаю, что вывод, к которому я пришел, - логика, которой нужно следовать. Что его следует использовать там, где ожидается перечисление, а не обязательно там, где может быть множественное перечисление.Если ваши данные всегда будут повторяемыми, возможно, не беспокойтесь об этом. Однако вы также можете развернуть его - это особенно полезно, если входящие данные могут быть большими (например, чтение с диска / сети):
Обратите внимание, что я немного изменил семантику DoSomethingElse, но это главным образом для демонстрации развернутого использования. Например, вы можете перемотать итератор. Вы также можете сделать это блоком итератора, что может быть неплохо; тогда нет
list
- и вы быyield return
предметы по мере их получения, а не добавляли в список для возврата.источник
Использование
IReadOnlyCollection<T>
илиIReadOnlyList<T>
в сигнатуре метода вместоIEnumerable<T>
, имеет то преимущество, что делает явным то, что вам может потребоваться проверить счетчик перед итерацией или выполнить итерацию несколько раз по какой-либо другой причине.Однако у них есть огромный недостаток, который вызовет проблемы, если вы попытаетесь изменить код для использования интерфейсов, например, чтобы сделать его более тестируемым и дружественным для динамического прокси. Ключевым моментом является то, что
IList<T>
он не наследуетсяIReadOnlyList<T>
и аналогично для других коллекций и их соответствующих интерфейсов только для чтения. (Короче говоря, это потому, что .NET 4.5 хотел сохранить совместимость ABI с более ранними версиями. Но они даже не воспользовались возможностью изменить это в ядре .NET. )Это означает, что если вы получили какой-
IList<T>
то фрагмент программы и хотите передать его в другую часть, которая ожидаетIReadOnlyList<T>
, вы не сможете! Вы можете однако передатьIList<T>
какIEnumerable<T>
.В конце концов,
IEnumerable<T>
это единственный интерфейс только для чтения, поддерживаемый всеми коллекциями .NET, включая все интерфейсы коллекций. Любая другая альтернатива вернется, чтобы укусить вас, когда вы поймете, что вы отстранены от некоторых архитектурных решений. Поэтому я думаю, что это правильный тип для использования в сигнатурах функций, чтобы выразить, что вы просто хотите коллекцию только для чтения.(Обратите внимание, что вы всегда можете написать
IReadOnlyList<T> ToReadOnly<T>(this IList<T> list)
метод расширения, который будет просто приводить , если базовый тип поддерживает оба интерфейса, но вы должны добавлять его вручную везде при рефакторинге, гдеIEnumerable<T>
это всегда совместимо.)Как всегда, это не является абсолютным, если вы пишете код, насыщенный базой данных, где случайное множественное перечисление может привести к катастрофе, вы можете предпочесть другой компромисс.
источник
Если цель на самом деле состоит в том, чтобы предотвратить множественные перечисления, то следует прочитать ответ Марка Гравелла, но, сохраняя ту же семантику, вы можете просто удалить избыточность
Any
иFirst
вызовы и продолжить:Обратите внимание, что это предполагает, что вы
IEnumerable
не универсальны или, по крайней мере, ограничены ссылочным типом.источник
objects
все еще передается в DoSomethingElse, который возвращает список, так что вероятность того, что вы все еще перечислите дважды, все еще существует. В любом случае, если коллекция была запросом LINQ to SQL, вы дважды попадаете в БД.First
выдает исключение для пустого списка. Я не думаю, что возможно сказать никогда не выбрасывать исключение в пустой список или всегда выбрасывать исключение в пустой список. Это зависит ...Я обычно перегружаю свой метод IEnumerable и IList в этой ситуации.
Я стараюсь объяснить в кратких комментариях методов, что вызов IEnumerable будет выполнять .ToList ().
Программист может выбрать .ToList () на более высоком уровне, если объединяются несколько операций, а затем вызвать перегрузку IList или позволить моей перегрузке IEnumerable позаботиться об этом.
источник
Если вам нужно проверить только первый элемент, вы можете посмотреть на него, не просматривая всю коллекцию:
источник
Во-первых, это предупреждение не всегда так много значит. Я обычно отключал его, убедившись, что это не горлышко бутылки производительности. Это просто означает, что
IEnumerable
оценивается дважды, что обычно не является проблемой, если толькоevaluation
само себе не занимает много времени. Даже если это займет много времени, в этом случае вы используете только один элемент в первый раз.В этом сценарии вы также можете использовать более мощные методы расширения linq.
В этом случае можно оценить только
IEnumerable
один раз с некоторыми трудностями, но сначала проанализировать и посмотреть, действительно ли это проблема.источник
IEnumerables
которые дают разные результаты каждый раз, когда вы выполняете итерацию, или это занимает очень много времени. См. Ответ Марка Грэйвелса - Он также заявляет, в первую очередь, «возможно, не волнуйтесь». Дело в том, что во многих случаях вы видите, что вам не о чем беспокоиться.