Я знаю, что могу использовать оператор for
и добиваться того же эффекта, но могу ли я прокручивать назад через цикл foreach
в С#?
Можно ли повторять прокрутку вперед через foreach?
Ответ 1
При работе со списком (прямая индексация) вы не можете сделать это так эффективно, как использовать цикл for
.
Изменить: что обычно означает, что когда вы можете использовать цикл for
, это, скорее всего, правильный метод для этой задачи. Плюс, поскольку реализовано in-order foreach
, сама конструкция построена для выражения циклов, которые не зависят от индексов элементов и порядка итераций, что особенно важно в параллельное программирование. По моему мнению, итерация, полагающаяся на порядок, не должна использовать foreach
для циклирования.
Ответ 2
Если вы используете .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, который никогда не перестает возвращать элементы.
Ответ 3
Как сказано в 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
будет "видеть" любые изменения сделанный в списке, пока вы повторяете. (Он также сломается, если вы удалите несколько элементов между итерациями.)
Для общих последовательностей нет пути повторения в обратном порядке - последовательность может быть бесконечной, например:
public static IEnumerable<T> GetStringsOfIncreasingSize()
{
string ret = "";
while (true)
{
yield return ret;
ret = ret + "x";
}
}
Что вы ожидаете, если попытаетесь перебрать это в обратном направлении?
Ответ 4
Если вы используете 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
Ответ 5
Прежде чем использовать foreach для итерации, отмените список методом "reverse":
myList.Reverse();
foreach( List listItem in myList)
{
Console.WriteLine(listItem);
}
Ответ 6
Нет. ForEach просто выполняет итерацию с помощью коллекции для каждого элемента и порядка, зависит от того, использует ли он IEnumerable или GetEnumerator().
Ответ 7
возможно, если вы можете изменить код коллекции, который реализует IEnumerable или IEnumerable (например, ваша собственная реализация IList).
Создайте Iterator, выполнив эту работу для вас, например, следующую реализацию через 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).
Ответ 8
Иногда у вас нет роскоши индексации или, возможно, вы хотите отменить результаты запроса Linq, или, может быть, вы не хотите изменять исходную коллекцию, если любой из них верен, Linq может помочь вы.
Метод расширения Linq с использованием анонимных типов с Linq Выберите, чтобы предоставить сортировку для 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"
Он называется "Инвертировать", потому что он является синонимом "Реверс" и позволяет устранить его с помощью функции "Реверсивный список".
Можно также отменить некоторые диапазоны коллекции, поскольку 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 по сравнению с использованием временного списка для обертывания коллекции для реверса.
На момент написания статьи я не знал о собственной реверсивной реализации Linq, тем не менее, было здорово работать над этим. https://msdn.microsoft.com/en-us/library/vstudio/bb358497(v=vs.100).aspx
Ответ 9
Я использовал этот код, который работал
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) {
Ответ 10
Это работает довольно хорошо
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]);
}