Я знаю, що я міг би використати 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 і не можете використовувати цикл (тобто у вас просто IEnumerable), вам просто доведеться написати свою функцію Reverse. Це має працювати:
static IEnumerable<T> Reverse<T>(IEnumerable<T> input)
{
return new Stack<T>(input);
}
Це спирається на деяку поведінку, яка, можливо, не настільки очевидна. Коли ви перейдете в IEnumerable до конструктора стека, він перейде через нього і висуне елементи на стек. Коли ви потім перебираєте стек, він вискакує назад у зворотному порядку.
Цей і Reverse()
спосіб розширення .NET 3.5 , очевидно, підірветься, якщо ви будете годувати його 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
"побачити" будь-які зміни, внесені до списку під час ітерації. (Він також зламається, якщо ви видалите кілька елементів між ітераціями.)
Для загальних послідовностей немає способу повторення в зворотному порядку - послідовність може бути нескінченною, наприклад:
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"
Він названий "Інвертувати", тому що він є синонімом "Зворотний" і дозволяє розбиратися з реалізацією списку Зворотній.
Можна також змінити певні діапазони колекції, оскільки 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
Якщо ви використовуєте Список <T>, ви також можете використовувати цей код:
List<string> list = new List<string>();
list.Add("1");
list.Add("2");
list.Add("3");
list.Reverse();
Це метод, який записує список зворотний сам по собі.
Тепер передбачення:
foreach(string s in list)
{
Console.WriteLine(s);
}
Вихід:
3
2
1
Це можливо, якщо ви можете змінити код колекції, який реалізує IEnumerable або IEnumerable (наприклад, ваша власна реалізація IList).
Створіть ітератор, який виконує цю роботу для вас, наприклад, як наступна реалізація через інтерфейс IEnumerable (якщо "пункти" є полем списку в цьому зразку):
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]);
}