Я знаю, что могу использовать for
оператор и добиться того же эффекта, но могу ли я выполнить цикл назад foreach
в C #?
IEnumerable<int> Infinity() { int i = 1; while (true) yield return i++; }
как бы вы это изменили?
Я знаю, что могу использовать for
оператор и добиться того же эффекта, но могу ли я выполнить цикл назад foreach
в C #?
IEnumerable<int> Infinity() { int i = 1; while (true) yield return i++; }
как бы вы это изменили?
Ответы:
При работе со списком (прямое индексирование) вы не можете сделать это так же эффективно, как использование for
цикла.
Изменить: что обычно означает, что когда вы можете использовать for
цикл, это, вероятно, правильный метод для этой задачи. Кроме того, поскольку foreach
эта конструкция реализована по порядку, сама конструкция предназначена для выражения циклов, которые не зависят от индексов элементов и порядка итераций, что особенно важно в параллельном программировании . Это мое мнение , что итерация опираясь на заказ не следует использовать foreach
для зацикливания.
yield return
".
Если вы используете .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 конструктору стека, он будет перебирать его и помещать элементы в стек. Когда вы затем перебираете стек, он возвращает вещи обратно в обратном порядке.
Этот Reverse()
метод и метод расширения .NET 3.5 , очевидно, взорвутся, если вы скармливаете ему IEnumerable, который никогда не прекращает возвращать элементы.
Как говорит 280Z28, IList<T>
вы можете просто использовать index. Вы можете скрыть это с помощью метода расширения:
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";
}
}
Что бы вы ожидали, если бы попытались повторить это в обратном порядке?
Перед использованием foreach
для итерации переверните список reverse
методом:
myList.Reverse();
foreach( List listItem in myList)
{
Console.WriteLine(listItem);
}
myList
это может быть полезно. IEnumerable.Reverse здесь не работает.
myList
похоже, относится к типу System.Collections.Generic.List<System.Windows.Documents.List>
(или любому другому настраиваемому List
типу), иначе этот код не работает: P
Иногда у вас нет роскоши индексирования, или, возможно, вы хотите отменить результаты запроса 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"
Он назван «Invert», потому что он является синонимом «Reverse» и позволяет устранять неоднозначность с помощью реализации 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
Если вы используете 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
Это возможно, если вы можете изменить код коллекции , реализующий IEnumerable или IEnumerable (например, вашу собственную реализацию IList).
Создайте Iterator, выполняющий эту работу за вас, например, как в следующей реализации через интерфейс IEnumerable (при условии, что 'items' является полем списка в этом примере):
public IEnumerator<TObject> GetEnumerator()
{
for (var i = items.Count - 1; i >= 0; i--)
{
yield return items[i];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
Из-за этого ваш список будет повторяться в обратном порядке по вашему списку.
Просто подсказка: вы должны четко указать это особое поведение вашего списка в документации (даже лучше, если вы выберете самоочевидное имя класса, такое как Stack или Queue).
Нет. ForEach просто выполняет итерацию по коллекции для каждого элемента, и порядок зависит от того, использует ли он IEnumerable или GetEnumerator ().
Немного проработав хороший ответ Джона Скита , он может быть универсальным:
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) {
.Reverse()
фактически изменяет список.
Это работает очень хорошо
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]);
}