ฉันรู้ว่าฉันสามารถใช้ไฟล์ 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 loop ได้ (เช่นคุณมี IEnumerable) คุณจะต้องเขียนฟังก์ชัน Reverse ของคุณเอง สิ่งนี้ควรใช้งานได้:
static IEnumerable<T> Reverse<T>(IEnumerable<T> input)
{
return new Stack<T>(input);
}
สิ่งนี้อาศัยพฤติกรรมบางอย่างซึ่งอาจไม่ชัดเจนนัก เมื่อคุณส่งผ่าน IE ไปยังตัวสร้างสแต็กไม่ได้มันจะวนซ้ำผ่านมันและดันรายการไปยังสแต็ก เมื่อคุณทำซ้ำในสแต็กมันจะปรากฏสิ่งต่างๆกลับออกมาในลำดับย้อนกลับ
วิธีนี้และ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 กลับไม่ทำงานที่นี่
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
หากคุณใช้รายการ <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' เป็นฟิลด์ 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 ด้วย)
ไม่ ForEach จะวนซ้ำผ่านคอลเลกชันสำหรับแต่ละรายการและคำสั่งซื้อขึ้นอยู่กับว่าใช้ IEnumerable หรือ GetEnumerator ()
การอธิบายอย่างละเอียดเล็กน้อยเกี่ยวกับคำตอบที่ดีของJon Skeetสิ่งนี้อาจเป็นประโยชน์:
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]);
}