Можно ли выполнить итерацию назад по foreach?
Вопрос
Я знаю, что мог бы использовать for
и добиться того же эффекта, но могу ли я выполнить цикл назад через foreach
цикл в С#?
Решение
При работе со списком (прямая индексация) вы не можете сделать это так же эффективно, как с помощью цикла for
.
Изменить. Обычно это означает, что когда вы можете использовать цикл foreach
, это, вероятно, правильный метод для этой задачи. Кроме того, поскольку <=> реализовано по порядку, сама конструкция создана для выражения циклов, которые не зависят от индексов элементов и порядка итераций, что особенно важно в параллельное программирование . Мое мнение , что итерация, основанная на порядке, не должна использовать <=> для цикла.
Другие советы
Если вы используете .NET 3.5, вы можете сделать это:
IEnumerable<int> enumerableThing = ...;
foreach (var x in enumerableThing.Reverse())
Он не очень эффективен, поскольку в основном он должен проходить через перечислитель вперед, помещая все в стек, а затем выводить все обратно в обратном порядке.
Если у вас есть непосредственно индексируемая коллекция (например, IList), вам определенно следует использовать цикл for
.
Если вы работаете в .NET 2.0 и не можете использовать цикл for (т. е. у вас просто есть IEnumerable), вам просто нужно написать собственную функцию Reverse. Это должно работать:
static IEnumerable<T> Reverse<T>(IEnumerable<T> input)
{
return new Stack<T>(input);
}
Это зависит от некоторого поведения, которое, возможно, не так очевидно. Когда вы передаете IEnumerable конструктору стека, он будет перебирать его и помещать элементы в стек. Когда вы затем выполняете итерацию в стеке, все всплывает в обратном порядке.
Это и метод расширения .NET 3.5 Reverse()
, очевидно, взорвутся, если вы передадите ему IEnumerable, который никогда не прекращает возвращать элементы.
Как говорит 280Z28, для IList<T>
вы можете просто использовать индекс. Вы можете скрыть это в методе расширения:
public static IEnumerable<T> FastReverse<T>(this IList<T> items)
{
for (int i = items.Count-1; i >= 0; i--)
{
yield return items[i];
}
}
Это будет быстрее, чем Enumerable.Reverse()
, который сначала буферизует все данные. (Я не верю, что Reverse
применяет какие-либо оптимизации так же, как Count()
.) Обратите внимание, что эта буферизация означает, что данные полностью читаются при первом запуске итерации, тогда как FastReverse
будет & Quot; см Quot &; любые изменения, внесенные в список во время итерации. (Он также сломается, если вы удалите несколько элементов между итерациями.)
Для общих последовательностей нет способа повторения в обратном порядке - последовательность может быть бесконечной, например:
public static IEnumerable<T> GetStringsOfIncreasingSize()
{
string ret = "";
while (true)
{
yield return ret;
ret = ret + "x";
}
}
Что бы вы ожидали, если бы попытались повторить это в обратном порядке?
Прежде чем использовать 'foreach' для итерации, переверните список методом 'reverse':
myList.Reverse();
foreach( List listItem in myList)
{
Console.WriteLine(listItem);
}
Если вы используете List < T >, вы также можете использовать этот код:
List<string> list = new List<string>();
list.Add("1");
list.Add("2");
list.Add("3");
list.Reverse();
Это метод, который записывает список обратно сам по себе.
Теперь foreach:
foreach(string s in list)
{
Console.WriteLine(s);
}
Вывод:
3
2
1
Иногда у вас нет такой роскоши, как индексирование, или, возможно, вы хотите отменить результаты запроса Linq, или, возможно, вы не хотите изменять исходную коллекцию. Если что-то из этого верно, Linq может вам помочь.
Метод расширения Linq, использующий анонимные типы с Linq Select для предоставления ключа сортировки для Linq OrderByDescending;
public static IEnumerable<T> Invert<T>(this IEnumerable<T> source)
{
var transform = source.Select(
(o, i) => new
{
Index = i,
Object = o
});
return transform.OrderByDescending(o => o.Index)
.Select(o => o.Object);
}
Использование:
var eable = new[]{ "a", "b", "c" };
foreach(var o in eable.Invert())
{
Console.WriteLine(o);
}
// "c", "b", "a"
Он называется «Инвертировать», потому что он является синонимом «Обратного» и позволяет устранить неоднозначность с помощью реализации List Reverse.
Также возможно перевернуть определенные диапазоны коллекции, поскольку Int32.MinValue и Int32.MaxValue находятся вне диапазона любого индекса коллекции, мы можем использовать их для процесса упорядочивания;если индекс элемента находится ниже заданного диапазона, ему присваивается Int32.MaxValue, чтобы его порядок не менялся при использовании OrderByDescending. Аналогично, элементам с индексом, превышающим заданный диапазон, будет присвоено Int32.MinValue, чтобы они появятся в конце процесса заказа.Всем элементам в данном диапазоне присваиваются их обычные индексы, и они соответственно меняются местами.
public static IEnumerable<T> Invert<T>(this IEnumerable<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 по сравнению с использованием временного списка для упаковки коллекции для реверсирования.
На момент написания я не знал о собственной реализации Reverse в Linq, тем не менее, было интересно над этим работать.https://msdn.microsoft.com/en-us/library/vstudio/bb358497(v=vs.100).aspx
Нет.ForEach просто перебирает коллекцию для каждого элемента, и порядок зависит от того, использует ли он IEnumerable или GetEnumerator().
Это возможно, если вы можете изменить код коллекции , который реализует IEnumerable или IEnumerable (например, ваша собственная реализация IList).
Создайте итератор выполняя эту работу за вас, например, как следующая реализация через Интерфейс IEnumerable (в этом примере предполагается, что 'items' - это поле List):
public IEnumerator<TObject> GetEnumerator()
{
for (var i = items.Count - 1; i >= 0; i--)
{
yield return items[i];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
Из-за этого ваш список будет перебирать ваш список в обратном порядке.
Просто подсказка: вам следует четко указать это специальное поведение вашего списка в документации (еще лучше, выбрав самообъясняющееся имя класса, например, Stack или Queue).
Обдумав хороший ответ от Джона Скита , это может быть универсально:
public static IEnumerable<T> Directional<T>(this IList<T> items, bool Forwards) {
if (Forwards) foreach (T item in items) yield return item;
else for (int i = items.Count-1; 0<=i; i--) yield return items[i];
}
А потом использовать как
foreach (var item in myList.Directional(forwardsCondition)) {
.
.
}
Я использовал этот код, который работал
if (element.HasAttributes) {
foreach(var attr in element.Attributes().Reverse())
{
if (depth > 1)
{
elements_upper_hierarchy_text = "";
foreach (var ancest in element.Ancestors().Reverse())
{
elements_upper_hierarchy_text += ancest.Name + "_";
}// foreach(var ancest in element.Ancestors())
}//if (depth > 1)
xml_taglist_report += " " + depth + " " + elements_upper_hierarchy_text+ element.Name + "_" + attr.Name +"(" + attr.Name +")" + " = " + attr.Value + "\r\n";
}// foreach(var attr in element.Attributes().Reverse())
}// if (element.HasAttributes) {
Это работает довольно хорошо
List<string> list = new List<string>();
list.Add("Hello");
list.Add("Who");
list.Add("Are");
list.Add("You");
foreach (String s in list)
{
Console.WriteLine(list[list.Count - list.IndexOf(s) - 1]);
}