Вам нужно будет создать экземпляры всех элементов в перечислении, чтобы вы могли изменить их порядок. Это может быть невозможно. Рассмотрим последовательность: IEnumerable<int> Infinity() { int i = 1; while (true) yield return i++; }как бы вы это изменили?
Suncat2000
Ответы:
85
При работе со списком (прямое индексирование) вы не можете сделать это так же эффективно, как использование forцикла.
Изменить: что обычно означает, что когда вы можете использовать forцикл, это, вероятно, правильный метод для этой задачи. Кроме того, поскольку foreachэта конструкция реализована по порядку, сама конструкция предназначена для выражения циклов, которые не зависят от индексов элементов и порядка итераций, что особенно важно в параллельном программировании . Это мое мнение , что итерация опираясь на заказ не следует использовать foreachдля зацикливания.
Я считаю ваше последнее заявление слишком общим. Конечно, бывают случаи, когда, например, какой-то IEnumerable нужно перебирать по порядку? Не стали бы вы в этом случае использовать foreach? Что бы вы использовали?
avl_sweden
1
ОБНОВЛЕНИЕ: см. [Ответ Брайана на аналогичный вопрос [( stackoverflow.com/a/3320924/199364 ) для более современного ответа с использованием Linq и обсуждения обоих списков и других перечисляемых элементов.
ToolmakerSteve
ОБНОВЛЕНИЕ: у Джона Скита есть еще более элегантный ответ, который теперь возможен с помощью " yield return".
ToolmakerSteve
2
У меня была та же первая реакция, что и у @avl_sweden - «итерация, основанная на порядке, не должна использовать foreach» звучит слишком широко. Да, foreach хорош для выражения параллельных задач, не зависящих от порядка. НО это также важная часть современного программирования на основе итераторов . Возможно, дело в том, что было бы яснее / безопаснее, если бы было два разных ключевых слова , чтобы прояснить, утверждает ли одно, что итерации не зависят от порядка? [Учитывая язык как Eiffel , которые могут распространяться контракты, такие утверждения могут быть доказуемо истинным или ложным, для данного кода.]
ToolmakerSteve
2
Идея о том, что foreach «построен для выражения циклов, не зависящих от индексов элементов и порядка итерации», неверна. Спецификация языка C # требует, чтобы элементы процесса foreach располагались по порядку. Либо с помощью MoveNext на итераторах, либо путем обработки индексов, начинающихся с нуля и увеличивающихся на единицу на каждой итерации массивов.
Дональд Рич
145
Если вы используете .NET 3.5, вы можете сделать это:
IEnumerable<int> enumerableThing =...;foreach(var x in enumerableThing.Reverse())
Это не очень эффективно, так как в основном он должен проходить через перечислитель вперед, помещая все в стек, а затем выталкивает все обратно в обратном порядке.
Если у вас есть непосредственно индексируемая коллекция (например, IList), вам определенно следует использовать forвместо нее цикл.
Если вы используете .NET 2.0 и не можете использовать цикл for (т. Е. У вас просто есть IEnumerable), вам просто нужно написать свою собственную функцию Reverse. Это должно работать:
Это зависит от некоторого поведения, которое, возможно, не так очевидно. Когда вы передаете IEnumerable конструктору стека, он будет перебирать его и помещать элементы в стек. Когда вы затем перебираете стек, он возвращает вещи обратно в обратном порядке.
Этот Reverse()метод и метод расширения .NET 3.5 , очевидно, взорвутся, если вы скармливаете ему IEnumerable, который никогда не прекращает возвращать элементы.
Мне что-то не хватает или ваше решение .Net 3.5 на самом деле не работает? Reverse () переворачивает список на место и не возвращает его. Жаль, что я надеялся на подобное решение.
user12861
2
Некоторые администраторы помечают этот ответ как НЕПРАВИЛЬНЫЙ, пожалуйста. Reverse () - это void [1], поэтому приведенный выше пример приводит к ошибке компиляции. [1] msdn.microsoft.com/en-us/library/b0axc2h2(v=vs.110).aspx
MickyD 03
6
@ user12861, Микки Дункан: ответ правильный, вы чего-то упускаете. В System.Collections.Generic.List <T> есть метод Reverse, который выполняет обратный ход на месте. В .Net 3.5 есть метод расширения для IEnumerable <T> с именем Reverse. Я изменил пример с var на IEnumerable <int>, чтобы сделать это более явным.
Мэтт Хауэллс
55
Как говорит 280Z28, IList<T>вы можете просто использовать index. Вы можете скрыть это с помощью метода расширения:
publicstaticIEnumerable<T>FastReverse<T>(thisIList<T> items){for(int i = items.Count-1; i >=0; i--){yieldreturn items[i];}}
Это будет быстрее, чем тот, Enumerable.Reverse()который буферизует все данные первым. (Я не верю, что Reverseбыли применены какие-либо оптимизации таким образом Count().) Обратите внимание, что эта буферизация означает, что данные полностью считываются при первом запуске итерации, тогда как во время итерации FastReverseбудут «видны» любые изменения, внесенные в список. (Он также сломается, если вы удалите несколько элементов между итерациями.)
Для обычных последовательностей нет возможности выполнить итерацию в обратном порядке - последовательность может быть бесконечной, например:
publicstaticIEnumerable<T>GetStringsOfIncreasingSize(){string ret ="";while(true){yieldreturn ret;
ret = ret +"x";}}
Что бы вы ожидали, если бы попытались повторить это в обратном порядке?
Просто любопытство, зачем использовать «> = 0» вместо «> -1»?
Chris S
15
> зачем использовать "> = 0" вместо "> -1"? Потому что> = 0 лучше передает намерение людям, читающим код. Компилятор должен иметь возможность оптимизировать это до эквивалента> -1, если это улучшит производительность.
Марк Маслар
1
FastReverse (это элементы IList <T>) должен быть FastReverse <T> (это элементы IList <T>. :)
Роб
Разделение палочек 0 <= i еще лучше. После обучения многих детей математике на протяжении многих лет и помощи людям с программированием, я обнаружил, что это значительно снижает количество ошибок, которые люди допускают, если ВСЕГДА меняют местами a> b на b <a, поскольку тогда все идет в естественном порядке строк. чисел (или ось X системы координат) - единственный недостаток заключается в том, что вам нужно вставить уравнение в XML, скажем .config
Эске Ран
13
Перед использованием foreachдля итерации переверните список reverseметодом:
myList.Reverse();foreach(List listItem in myList){Console.WriteLine(listItem);}
Небольшое предупреждение о том, что myListэто может быть полезно. IEnumerable.Reverse здесь не работает.
nawfal
2
@ MA-Maddin нет, они разные. Я предполагаю, что ответчик полагается на List <T> .Reverse, который существует.
nawfal
На самом деле, я не знаю, почему я это сказал. Я должен был добавить больше деталей ... В любом случае, это сбивает с толку код, поскольку он, myListпохоже, относится к типу System.Collections.Generic.List<System.Windows.Documents.List>(или любому другому настраиваемому Listтипу), иначе этот код не работает: P
Мартин Шнайдер
5
Иногда у вас нет роскоши индексирования, или, возможно, вы хотите отменить результаты запроса Linq, или, может быть, вы не хотите изменять исходную коллекцию, если что-то из этого верно, Linq может вам помочь.
Метод расширения Linq, использующий анонимные типы с Linq Select, чтобы предоставить ключ сортировки для Linq OrderByDescending;
var eable =new[]{"a","b","c"};foreach(var o in eable.Invert()){Console.WriteLine(o);}// "c", "b", "a"
Он назван «Invert», потому что он является синонимом «Reverse» и позволяет устранять неоднозначность с помощью реализации List Reverse.
Также возможно изменить определенные диапазоны коллекции, поскольку Int32.MinValue и Int32.MaxValue находятся вне диапазона какого-либо индекса коллекции, мы можем использовать их для процесса заказа; если индекс элемента ниже заданного диапазона, ему назначается Int32.MaxValue, чтобы его порядок не изменялся при использовании OrderByDescending; аналогично элементам с индексом, превышающим данный диапазон, будет назначен Int32.MinValue, чтобы они появляются до конца процесса заказа. Всем элементам в данном диапазоне присваивается их нормальный индекс, и они соответственно меняются местами.
publicstaticIEnumerable<T>Invert<T>(thisIEnumerable<T> source,int index,int count){var transform = source.Select((o, i)=>new{Index= i < index ?Int32.MaxValue: i >= index + count ?Int32.MinValue: i,Object= o
});return transform.OrderByDescending(o => o.Index).Select(o => o.Object);}
Использование:
var eable =new[]{"a","b","c","d"};foreach(var o in eable.Invert(1,2)){Console.WriteLine(o);}// "a", "c", "b", "d"
Я не уверен в производительности этих реализаций Linq по сравнению с использованием временного списка для обертывания коллекции для реверсирования.
Это возможно, если вы можете изменить код коллекции , реализующий IEnumerable или IEnumerable (например, вашу собственную реализацию IList).
Создайте Iterator, выполняющий эту работу за вас, например, как в следующей реализации через интерфейс IEnumerable (при условии, что 'items' является полем списка в этом примере):
publicIEnumerator<TObject>GetEnumerator(){for(var i = items.Count-1; i >=0; i--){yieldreturn items[i];}}IEnumeratorIEnumerable.GetEnumerator(){returnGetEnumerator();}
Из-за этого ваш список будет повторяться в обратном порядке по вашему списку.
Просто подсказка: вы должны четко указать это особое поведение вашего списка в документации (даже лучше, если вы выберете самоочевидное имя класса, такое как Stack или Queue).
Нет. ForEach просто выполняет итерацию по коллекции для каждого элемента, и порядок зависит от того, использует ли он IEnumerable или GetEnumerator ().
Это плохо, потому что .Reverse()фактически изменяет список.
Колин Баснетт
-8
Это работает очень хорошо
List<string>list=newList<string>();list.Add("Hello");list.Add("Who");list.Add("Are");list.Add("You");foreach(String s inlist){Console.WriteLine(list[list.Count-list.IndexOf(s)-1]);}
IEnumerable<int> Infinity() { int i = 1; while (true) yield return i++; }
как бы вы это изменили?Ответы:
При работе со списком (прямое индексирование) вы не можете сделать это так же эффективно, как использование
for
цикла.Изменить: что обычно означает, что когда вы можете использовать
for
цикл, это, вероятно, правильный метод для этой задачи. Кроме того, посколькуforeach
эта конструкция реализована по порядку, сама конструкция предназначена для выражения циклов, которые не зависят от индексов элементов и порядка итераций, что особенно важно в параллельном программировании . Это мое мнение , что итерация опираясь на заказ не следует использоватьforeach
для зацикливания.источник
yield return
".Если вы используете .NET 3.5, вы можете сделать это:
Это не очень эффективно, так как в основном он должен проходить через перечислитель вперед, помещая все в стек, а затем выталкивает все обратно в обратном порядке.
Если у вас есть непосредственно индексируемая коллекция (например, IList), вам определенно следует использовать
for
вместо нее цикл.Если вы используете .NET 2.0 и не можете использовать цикл for (т. Е. У вас просто есть IEnumerable), вам просто нужно написать свою собственную функцию Reverse. Это должно работать:
Это зависит от некоторого поведения, которое, возможно, не так очевидно. Когда вы передаете IEnumerable конструктору стека, он будет перебирать его и помещать элементы в стек. Когда вы затем перебираете стек, он возвращает вещи обратно в обратном порядке.
Этот
Reverse()
метод и метод расширения .NET 3.5 , очевидно, взорвутся, если вы скармливаете ему IEnumerable, который никогда не прекращает возвращать элементы.источник
Как говорит 280Z28,
IList<T>
вы можете просто использовать index. Вы можете скрыть это с помощью метода расширения:Это будет быстрее, чем тот,
Enumerable.Reverse()
который буферизует все данные первым. (Я не верю, чтоReverse
были применены какие-либо оптимизации таким образомCount()
.) Обратите внимание, что эта буферизация означает, что данные полностью считываются при первом запуске итерации, тогда как во время итерацииFastReverse
будут «видны» любые изменения, внесенные в список. (Он также сломается, если вы удалите несколько элементов между итерациями.)Для обычных последовательностей нет возможности выполнить итерацию в обратном порядке - последовательность может быть бесконечной, например:
Что бы вы ожидали, если бы попытались повторить это в обратном порядке?
источник
Перед использованием
foreach
для итерации переверните списокreverse
методом:источник
myList
это может быть полезно. IEnumerable.Reverse здесь не работает.myList
похоже, относится к типуSystem.Collections.Generic.List<System.Windows.Documents.List>
(или любому другому настраиваемомуList
типу), иначе этот код не работает: PИногда у вас нет роскоши индексирования, или, возможно, вы хотите отменить результаты запроса Linq, или, может быть, вы не хотите изменять исходную коллекцию, если что-то из этого верно, Linq может вам помочь.
Метод расширения Linq, использующий анонимные типы с Linq Select, чтобы предоставить ключ сортировки для Linq OrderByDescending;
Использование:
Он назван «Invert», потому что он является синонимом «Reverse» и позволяет устранять неоднозначность с помощью реализации List Reverse.
Также возможно изменить определенные диапазоны коллекции, поскольку Int32.MinValue и Int32.MaxValue находятся вне диапазона какого-либо индекса коллекции, мы можем использовать их для процесса заказа; если индекс элемента ниже заданного диапазона, ему назначается Int32.MaxValue, чтобы его порядок не изменялся при использовании OrderByDescending; аналогично элементам с индексом, превышающим данный диапазон, будет назначен Int32.MinValue, чтобы они появляются до конца процесса заказа. Всем элементам в данном диапазоне присваивается их нормальный индекс, и они соответственно меняются местами.
Использование:
Я не уверен в производительности этих реализаций Linq по сравнению с использованием временного списка для обертывания коллекции для реверсирования.
На момент написания я не знал о собственной реализации Reverse Linq, тем не менее, было весело работать над этим. https://msdn.microsoft.com/en-us/library/vstudio/bb358497(v=vs.100).aspx
источник
Если вы используете List <T>, вы также можете использовать этот код:
Это метод, который записывает список в обратном порядке.
Теперь foreach:
Результат:
источник
Это возможно, если вы можете изменить код коллекции , реализующий IEnumerable или IEnumerable (например, вашу собственную реализацию IList).
Создайте Iterator, выполняющий эту работу за вас, например, как в следующей реализации через интерфейс IEnumerable (при условии, что 'items' является полем списка в этом примере):
Из-за этого ваш список будет повторяться в обратном порядке по вашему списку.
Просто подсказка: вы должны четко указать это особое поведение вашего списка в документации (даже лучше, если вы выберете самоочевидное имя класса, такое как Stack или Queue).
источник
Нет. ForEach просто выполняет итерацию по коллекции для каждого элемента, и порядок зависит от того, использует ли он IEnumerable или GetEnumerator ().
источник
Немного проработав хороший ответ Джона Скита , он может быть универсальным:
А затем используйте как
источник
Я использовал этот код, который работал
источник
.Reverse()
фактически изменяет список.Это работает очень хорошо
источник