foreach vs someList.ForEach () {}


167

เห็นได้ชัดว่ามีหลายวิธีในการย้ำผ่านคอลเลกชัน อยากรู้อยากเห็นหากมีความแตกต่างใด ๆ หรือทำไมคุณต้องใช้อีกทางหนึ่งมากกว่า

ประเภทแรก:

List<string> someList = <some way to init>
foreach(string s in someList) {
   <process the string>
}

วิธีอื่น ๆ :

List<string> someList = <some way to init>
someList.ForEach(delegate(string s) {
    <process the string>
});

ฉันเดาว่าส่วนบนของหัวของฉันแทนที่จะเป็นตัวแทนที่ไม่ระบุชื่อที่ฉันใช้ด้านบนคุณจะมีตัวแทนที่นำกลับมาใช้ใหม่ได้คุณสามารถระบุ ...


1
ฉันขอแนะนำให้อ่านEric Lipperts บล็อก "foreach" กับ "ForEach"
Erik Philips

คำตอบ:


134

มีความแตกต่างที่สำคัญอย่างหนึ่งและมีประโยชน์คือระหว่างสองสิ่งนี้

เนื่องจาก. ForEach ใช้การforวนซ้ำเพื่อทำซ้ำคอลเลกชันสิ่งนี้เป็นสิ่งที่ถูกต้อง (แก้ไข: ก่อนหน้า. net 4.5 - การใช้งานเปลี่ยนไปและพวกเขาทั้งสองโยน):

someList.ForEach(x => { if(x.RemoveMe) someList.Remove(x); }); 

ในขณะที่foreachใช้ตัวแจงนับดังนั้นสิ่งนี้จึงไม่ถูกต้อง:

foreach(var item in someList)
  if(item.RemoveMe) someList.Remove(item);

tl; dr: อย่าคัดลอกโค้ดนี้ในใบสมัครของคุณ!

ตัวอย่างเหล่านี้ได้รับการปฏิบัติที่ดีที่สุดไม่ได้พวกเขาเป็นเพียงแสดงให้เห็นถึงความแตกต่างระหว่างและForEach()foreach

การลบรายการออกจากรายการภายในforลูปอาจมีผลข้างเคียง คำอธิบายที่พบบ่อยที่สุดในความคิดเห็นของคำถามนี้

โดยทั่วไปหากคุณต้องการลบหลายรายการจากรายการคุณจะต้องแยกการกำหนดว่าจะลบรายการใดออกจากการลบจริง มันไม่ได้ทำให้โค้ดของคุณมีขนาดกะทัดรัด แต่รับประกันได้ว่าคุณจะไม่พลาดรายการใด ๆ


35
แม้แล้วคุณควรใช้ someList.RemoveAll (x => x.RemoveMe) แทน
Mark Cidade

2
ด้วย Linq ทุกสิ่งสามารถทำได้ดีกว่า ฉันเพิ่งแสดงตัวอย่างของการปรับเปลี่ยนคอลเลกชันภายใน foreach ...

2
RemoveAll () เป็นวิธีการในรายการ <T>
Mark Cidade

3
คุณอาจตระหนักถึงเรื่องนี้ แต่ผู้คนควรระวังในการลบรายการด้วยวิธีนี้ หากคุณลบรายการ N การทำซ้ำจะข้ามไปที่รายการ (N + 1) และคุณจะไม่เห็นรายการนั้นในตัวแทนของคุณหรือมีโอกาสที่จะลบออกเช่นเดียวกับที่คุณทำในวงวนเอง
El Zorko

9
หากคุณวนซ้ำรายการย้อนหลังด้วย for for loop คุณสามารถลบรายการโดยไม่มีปัญหาการเลื่อนดัชนี
S. TarıkÇetin

78

เรามีรหัสบางส่วนที่นี่ (ใน VS2005 และ C # 2.0) ซึ่งวิศวกรก่อนหน้าใช้วิธีของพวกเขาlist.ForEach( delegate(item) { foo;});แทนการใช้foreach(item in list) {foo; };รหัสทั้งหมดที่พวกเขาเขียน เช่นกลุ่มของรหัสสำหรับอ่านแถวจาก dataReader

ฉันยังไม่รู้ว่าทำไมพวกเขาถึงทำอย่างนี้

ข้อเสียของlist.ForEach()คือ:

  • มันละเอียดมากขึ้นใน C # 2.0 อย่างไรก็ตามใน C # 3 เป็นต้นไปคุณสามารถใช้=>ไวยากรณ์ "" เพื่อสร้างนิพจน์ที่เข้าใจได้ง่าย

  • มันไม่ค่อยคุ้นเคย คนที่ต้องรักษารหัสนี้จะสงสัยว่าทำไมคุณทำอย่างนั้น ฉันใช้เวลาสักครู่ในการตัดสินใจว่าไม่มีเหตุผลใด ๆ ยกเว้นอาจจะทำให้นักเขียนดูฉลาด (คุณภาพของส่วนที่เหลือของรหัสที่ทำลายนั้น) นอกจากนี้ยังสามารถอ่านได้น้อยลงด้วย " })" ที่ส่วนท้ายของบล็อกรหัสของผู้รับมอบสิทธิ์

  • ดูหนังสือของ Bill Wagner "Effective C #: 50 วิธีพิเศษในการปรับปรุง C # ของคุณ" ซึ่งเขาพูดถึงว่าทำไม foreach ถึงชอบลูปอื่น ๆ เช่นหรือขณะลูป - ประเด็นหลักคือคุณต้องให้คอมไพเลอร์ตัดสินใจวิธีที่ดีที่สุดในการสร้าง ห่วง หากคอมไพเลอร์เวอร์ชันอนาคตสามารถใช้เทคนิคที่เร็วกว่าได้คุณจะได้รับสิ่งนี้ฟรีโดยใช้ foreach และสร้างใหม่แทนที่จะเปลี่ยนรหัสของคุณ

  • foreach(item in list)สร้างช่วยให้คุณใช้breakหรือcontinueถ้าคุณจำเป็นต้องออกจากการทำซ้ำหรือห่วง แต่คุณไม่สามารถแก้ไขรายการภายในลูป foreach

ฉันประหลาดใจที่เห็นว่า list.ForEachเร็วขึ้นเล็กน้อย แต่นั่นอาจไม่ใช่เหตุผลที่ถูกต้องที่จะใช้มันตลอดซึ่งจะเป็นการเพิ่มประสิทธิภาพก่อนวัยอันควร หากแอปพลิเคชันของคุณใช้ฐานข้อมูลหรือบริการบนเว็บที่ไม่ใช่การควบคุมลูปจะเกือบตลอดเวลา และคุณเคยเปรียบเทียบกับforลูปหรือไม่? list.ForEachอาจจะเร็วขึ้นเนื่องจากการใช้ที่ภายในและforห่วงโดยไม่ต้องห่อหุ้มจะได้เร็วยิ่งขึ้น

ฉันไม่เห็นด้วยที่ list.ForEach(delegate)รุ่นนี้มี "การทำงานมากขึ้น" ในทางใดทางหนึ่งที่สำคัญ มันส่งผ่านฟังก์ชั่นไปยังฟังก์ชั่น แต่ไม่มีความแตกต่างใหญ่ในผลลัพธ์หรือการจัดโปรแกรม

ฉันไม่คิดว่าforeach(item in list)"พูดอย่างที่คุณต้องการ" - กfor(int 1 = 0; i < count; i++)ลูปทำอย่างนั้นforeachวนซ้ำจะเป็นทางเลือกในการควบคุมคอมไพเลอร์

ความรู้สึกของฉันคือในโครงการใหม่ที่จะใช้foreach(item in list)สำหรับลูปส่วนใหญ่เพื่อให้เป็นไปตามการใช้งานทั่วไปและเพื่อให้สามารถอ่านได้และใช้list.Foreach()สำหรับบล็อกสั้นเท่านั้นเมื่อคุณสามารถทำบางสิ่งได้อย่างหรูหราหรือกะทัดรัดด้วยตัวดำเนินการ C # 3 " =>" ในกรณีเช่นว่ามีอยู่แล้วอาจจะเป็นวิธีขยาย LINQ ForEach()ที่เป็นเฉพาะมากกว่า ดูว่าWhere(), Select(), Any(), All(), Max()หรือหนึ่งในหลายวิธี LINQ อื่น ๆ ไม่ได้แล้วทำในสิ่งที่คุณต้องการจากวง


เพียงแค่อยากรู้อยากเห็น ... ดูที่การใช้งานของ Microsoft ... Referencesource.microsoft.com/#mscorlib/system/collections/…
Alexandre

17

เพื่อความสนุกสนานฉันดึงรายการลงในตัวสะท้อนแสงและนี่คือผลลัพธ์ C #:

public void ForEach(Action<T> action)
{
    if (action == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
    }
    for (int i = 0; i < this._size; i++)
    {
        action(this._items[i]);
    }
}

ในทำนองเดียวกัน MoveNext in Enumerator ซึ่งเป็นสิ่งที่ใช้โดย foreach คือ:

public bool MoveNext()
{
    if (this.version != this.list._version)
    {
        ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
    }
    if (this.index < this.list._size)
    {
        this.current = this.list._items[this.index];
        this.index++;
        return true;
    }
    this.index = this.list._size + 1;
    this.current = default(T);
    return false;
}

List.ForEach นั้นถูกตัดทอนลงมากกว่า MoveNext ซึ่งมีการประมวลผลที่น้อยกว่าซึ่งจะทำให้ JIT เป็นสิ่งที่มีประสิทธิภาพมากขึ้น

นอกจากนี้ foreach () จะจัดสรร Enumerator ใหม่ไม่ว่าจะเกิดอะไรขึ้น GC คือเพื่อนของคุณ แต่ถ้าคุณกำลังทำสิ่งเดียวกันนี้ซ้ำ ๆ สิ่งนี้จะทำให้วัตถุที่ถูกเหวี่ยงออกไปมากขึ้นเมื่อเทียบกับการใช้ผู้แทนซ้ำ - BUT - นี่เป็นกรณีที่จริง ในการใช้งานทั่วไปคุณจะเห็นความแตกต่างเล็กน้อยหรือไม่มีเลย


1
คุณไม่รับประกันว่ารหัสที่สร้างโดย foreach จะเหมือนกันระหว่างเวอร์ชันคอมไพเลอร์ รหัสที่สร้างขึ้นอาจได้รับการปรับปรุงโดยรุ่นในอนาคต
Anthony

1
ในฐานะ. NET Core 3.1 ForEach นั้นยังเร็วกว่า
jackmott

13

ฉันรู้สองสิ่งที่คลุมเครือ - ish ที่ทำให้พวกเขาแตกต่างกัน ไปหาฉัน!

ประการแรกมีข้อผิดพลาดแบบคลาสสิกในการมอบสิทธิ์สำหรับแต่ละรายการในรายการ หากคุณใช้คำสำคัญ foreach ผู้รับมอบสิทธิ์ทั้งหมดของคุณจะสามารถอ้างอิงถึงรายการสุดท้ายของรายการ:

    // A list of actions to execute later
    List<Action> actions = new List<Action>();

    // Numbers 0 to 9
    List<int> numbers = Enumerable.Range(0, 10).ToList();

    // Store an action that prints each number (WRONG!)
    foreach (int number in numbers)
        actions.Add(() => Console.WriteLine(number));

    // Run the actions, we actually print 10 copies of "9"
    foreach (Action action in actions)
        action();

    // So try again
    actions.Clear();

    // Store an action that prints each number (RIGHT!)
    numbers.ForEach(number =>
        actions.Add(() => Console.WriteLine(number)));

    // Run the actions
    foreach (Action action in actions)
        action();

กระบวนการ List.ForEach ไม่มีปัญหานี้ รายการปัจจุบันของการวนซ้ำถูกส่งผ่านตามค่าเป็นอาร์กิวเมนต์ไปยังแลมบ์ดาภายนอกแล้วแลมบ์ดาภายในจับอาร์กิวเมนต์นั้นได้อย่างถูกต้องในการปิดของมันเอง แก้ไขปัญหา.

(น่าเศร้าที่ฉันเชื่อว่า ForEach เป็นสมาชิกของ List แทนที่จะเป็นวิธีการขยายแม้ว่ามันจะง่ายในการกำหนดด้วยตัวคุณเองดังนั้นคุณจึงมีเครื่องมือนี้ในทุกประเภท

ประการที่สองวิธีการ ForEach มีข้อ จำกัด หากคุณใช้ IEnumerable โดยใช้การคืนผลตอบแทนคุณจะไม่สามารถรับคืนผลตอบแทนภายในแลมบ์ดาได้ ดังนั้นการวนซ้ำไอเท็มในคอลเลคชั่นเพื่อที่จะให้ผลตอบแทนเป็นไปไม่ได้ด้วยวิธีนี้ คุณจะต้องใช้คำหลัก foreach และแก้ไขปัญหาการปิดโดยการทำสำเนาค่าลูปปัจจุบันภายในลูปด้วยตนเอง

เพิ่มเติมที่นี่


5
ปัญหาที่คุณพูดถึงforeachคือ "แก้ไข" ใน C # 5 stackoverflow.com/questions/8898925//
Stri StriWarrior

13

ฉันเดาว่าการsomeList.ForEach()โทรนั้นสามารถทำการขนานได้อย่างง่ายดายในขณะที่การโทรแบบขนานforeachนั้นไม่ใช่เรื่องง่าย foreachคุณสามารถทำงานได้อย่างง่ายดายได้รับมอบหมายที่แตกต่างกันหลายแกนแตกต่างกันซึ่งไม่ใช่เรื่องง่ายที่จะทำอย่างไรกับปกติ
แค่ 2 เซ็นต์ของฉัน


2
ฉันคิดว่าเขาหมายความว่าเอ็นจิ้นรันไทม์สามารถทำให้มันขนานกันโดยอัตโนมัติ มิฉะนั้นทั้ง foreach และ. ForEach สามารถขนานกันด้วยมือโดยใช้เธรดจากพูลในผู้แทนการดำเนินการแต่ละคน
Isak Savo

@Isak แต่นั่นจะเป็นข้อสันนิษฐานที่ไม่ถูกต้อง หากวิธีการที่ไม่ระบุชื่อรอกท้องถิ่นหรือสมาชิกรันไทม์จะไม่ 8 อย่างง่ายดาย) สามารถทำให้เป็นอัมพาต
Rune FS

6

อย่างที่พวกเขาบอกว่าปีศาจอยู่ในรายละเอียด ...

ความแตกต่างที่ใหญ่ที่สุดระหว่างทั้งสองวิธีของการแจงนับการรวบรวมคือการforeachดำเนินการรัฐในขณะที่ForEach(x => { })ไม่

แต่ให้ขุดลึกลงไปสักหน่อยเพราะมีบางสิ่งที่คุณควรระวังซึ่งอาจมีผลต่อการตัดสินใจของคุณและมีคำเตือนบางประการที่คุณควรระวังเมื่อทำการเข้ารหัสสำหรับกรณีใดกรณีหนึ่ง

ให้ใช้List<T>ในการทดลองเล็กน้อยเพื่อสังเกตพฤติกรรม สำหรับการทดลองนี้ฉันใช้. NET 4.7.2:

var names = new List<string>
{
    "Henry",
    "Shirley",
    "Ann",
    "Peter",
    "Nancy"
};

ให้ทำซ้ำสิ่งนี้ด้วยสิ่งforeachแรก:

foreach (var name in names)
{
    Console.WriteLine(name);
}

เราสามารถขยายสิ่งนี้เป็น:

using (var enumerator = names.GetEnumerator())
{

}

ด้วยตัวแจงนับอยู่ในมือมองภายใต้ฝาครอบที่เราได้รับ:

public List<T>.Enumerator GetEnumerator()
{
  return new List<T>.Enumerator(this);
}
    internal Enumerator(List<T> list)
{
  this.list = list;
  this.index = 0;
  this.version = list._version;
  this.current = default (T);
}

public bool MoveNext()
{
  List<T> list = this.list;
  if (this.version != list._version || (uint) this.index >= (uint) list._size)
    return this.MoveNextRare();
  this.current = list._items[this.index];
  ++this.index;
  return true;
}

object IEnumerator.Current
{
  {
    if (this.index == 0 || this.index == this.list._size + 1)
      ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);
    return (object) this.Current;
  }
}

มีสองสิ่งที่ชัดเจนในทันที:

  1. เราถูกส่งคืนวัตถุที่เป็นรัฐพร้อมกับความรู้ที่ลึกซึ้งเกี่ยวกับคอลเล็กชั่นพื้นฐาน
  2. สำเนาของชุดสะสมเป็นสำเนาที่ตื้น

แน่นอนว่าไม่มีวิธีที่ปลอดภัยสำหรับเธรด ดังที่ได้กล่าวไว้ข้างต้นการเปลี่ยนแปลงคอลเลกชันในขณะที่การทำซ้ำนั้นเป็นเพียงโมโจที่ไม่ดี

แต่สิ่งที่เกี่ยวกับปัญหาของคอลเลกชันกลายเป็นโมฆะในระหว่างการทำซ้ำโดยวิธีการนอกเราล้อเล่นกับคอลเลกชันในระหว่างการทำซ้ำ? แนวทางปฏิบัติที่ดีที่สุดแนะนำการกำหนดเวอร์ชันคอลเลกชันระหว่างการดำเนินการและการวนซ้ำและการตรวจสอบเวอร์ชันเพื่อตรวจสอบเมื่อการเปลี่ยนแปลงคอลเลกชันพื้นฐาน

นี่คือสิ่งที่มืดมนจริงๆ ตามเอกสารของ Microsoft:

หากมีการเปลี่ยนแปลงคอลเลกชันเช่นเพิ่มแก้ไขหรือลบองค์ประกอบลักษณะการทำงานของตัวแจงนับจะไม่ได้กำหนด

นั่นหมายความว่าอย่างไร ตัวอย่างเช่นเพียงเพราะList<T>ใช้การจัดการข้อยกเว้นไม่ได้หมายความว่าคอลเลกชันทั้งหมดที่ใช้IList<T>จะทำแบบเดียวกัน นี่ดูเหมือนจะเป็นการละเมิดหลักการแทนที่ Liskov อย่างชัดเจน:

วัตถุของซูเปอร์คลาสจะต้องถูกแทนที่ด้วยวัตถุของคลาสย่อยโดยไม่ทำลายแอปพลิเคชัน

ปัญหาอีกประการหนึ่งคือตัวแจงนับต้องใช้งานIDisposable- นั่นหมายถึงแหล่งอื่นของหน่วยความจำที่อาจเกิดการรั่วไหลไม่เพียง แต่ถ้าผู้โทรได้รับมันผิด แต่ถ้าผู้เขียนไม่ได้ใช้Disposeเท่านั้น

สุดท้ายเรามีปัญหาตลอดชีวิต ... จะเกิดอะไรขึ้นถ้าตัววนซ้ำถูกต้อง แต่การรวบรวมที่พื้นฐานหายไป? ตอนนี้เรามีภาพรวมของสิ่งที่เป็น ... เมื่อคุณแยกอายุการใช้งานของคอลเลกชันและตัววนซ้ำคุณจะถามถึงปัญหา

ตอนนี้ให้ตรวจสอบForEach(x => { }):

names.ForEach(name =>
{

});

สิ่งนี้ขยายเป็น:

public void ForEach(Action<T> action)
{
  if (action == null)
    ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
  int version = this._version;
  for (int index = 0; index < this._size && (version == this._version || !BinaryCompatibility.TargetsAtLeast_Desktop_V4_5); ++index)
    action(this._items[index]);
  if (version == this._version || !BinaryCompatibility.TargetsAtLeast_Desktop_V4_5)
    return;
  ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
}

สิ่งสำคัญที่ควรทราบมีดังต่อไปนี้:

for (int index = 0; index < this._size && ... ; ++index) action(this._items[index]);

รหัสนี้ไม่ได้จัดสรรตัวแจงนับใด ๆ (ไม่มีให้Dispose) และจะไม่หยุดชั่วคราวในขณะที่วนซ้ำ

โปรดทราบว่าสิ่งนี้ยังทำสำเนาตื้นของคอลเลกชันพื้นฐาน แต่ตอนนี้คอลเลกชันเป็นภาพรวมในเวลา หากผู้เขียนไม่ได้ใช้การตรวจสอบอย่างถูกต้องสำหรับคอลเลกชันที่เปลี่ยนไปหรือกำลัง 'ค้าง' สแน็ปช็อตก็ยังคงใช้ได้

สิ่งนี้ไม่ได้ป้องกันคุณจากปัญหาเรื่องอายุการใช้งาน ... หากคอลเล็กชันต้นแบบหายไปตอนนี้คุณมีสำเนาตื้น ๆ ที่ชี้ไปยังสิ่งที่เคยเป็น ... แต่อย่างน้อยคุณก็ไม่มี Disposeปัญหา จัดการกับผู้ทำซ้ำที่กำพร้า ...

ใช่ฉันพูดซ้ำแล้วซ้ำอีก ... บางครั้งมันก็เป็นประโยชน์ที่จะมีสถานะ สมมติว่าคุณต้องการรักษาบางสิ่งบางอย่างให้เหมือนกับเคอร์เซอร์ฐานข้อมูล ... อาจมีหลายforeachสไตล์Iterator<T>ก็เป็นวิธีที่จะไป ฉันไม่ชอบสไตล์การออกแบบนี้เนื่องจากมีปัญหาตลอดชีวิตมากเกินไปและคุณต้องพึ่งพาความดีของผู้แต่งคอลเลกชันที่คุณต้องพึ่งพา (เว้นแต่คุณจะเขียนทุกอย่างเองตั้งแต่ต้น)

มีตัวเลือกที่สามอยู่เสมอ ...

for (var i = 0; i < names.Count; i++)
{
    Console.WriteLine(names[i]);
}

มันไม่เซ็กซี่ แต่มันมีฟัน (ขอโทษที่Tom CruiseและThe Firm )

มันเป็นทางเลือกของคุณ แต่ตอนนี้คุณรู้แล้วและมันสามารถเป็นข้อมูล


5

คุณสามารถตั้งชื่อผู้รับมอบสิทธิ์นิรนาม :-)

และคุณสามารถเขียนวินาทีเป็น:

someList.ForEach(s => s.ToUpper())

ซึ่งฉันชอบและประหยัดการพิมพ์จำนวนมาก

ดังที่ Joachim กล่าวว่าการขนานจะง่ายกว่าที่จะนำไปใช้กับรูปแบบที่สอง


2

เบื้องหลังผู้ร่วมประชุมที่ไม่ระบุชื่อจะถูกเปลี่ยนเป็นวิธีการจริงเพื่อให้คุณสามารถมีค่าใช้จ่ายด้วยตัวเลือกที่สองหากคอมไพเลอร์ไม่ได้เลือกที่จะอินไลน์ฟังก์ชั่น นอกจากนี้ตัวแปรโลคัลใด ๆ ที่อ้างอิงโดยเนื้อความของตัวอย่างผู้รับมอบสิทธิ์แบบไม่ระบุชื่อจะเปลี่ยนแปลงตามธรรมชาติเนื่องจากกลวิธีคอมไพเลอร์เพื่อซ่อนความจริงที่ว่าได้รับการรวบรวมเป็นวิธีการใหม่ ข้อมูลเพิ่มเติมที่นี่เกี่ยวกับวิธีการที่ C # ใช้เวทมนตร์นี้:

http://blogs.msdn.com/oldnewthing/archive/2006/08/04/688527.aspx


2

ฟังก์ชัน ForEach เป็นสมาชิกของรายการคลาสทั่วไป

ฉันได้สร้างส่วนขยายต่อไปนี้เพื่อทำซ้ำรหัสภายใน:

public static class MyExtension<T>
    {
        public static void MyForEach(this IEnumerable<T> collection, Action<T> action)
        {
            foreach (T item in collection)
                action.Invoke(item);
        }
    }

ท้ายที่สุดเราใช้ foreach ปกติ (หรือวนรอบถ้าคุณต้องการ)

ในทางกลับกันการใช้ฟังก์ชั่นของผู้แทนเป็นอีกวิธีหนึ่งในการกำหนดฟังก์ชั่นรหัสนี้:

delegate(string s) {
    <process the string>
}

เทียบเท่ากับ:

private static void myFunction(string s, <other variables...>)
{
   <process the string>
}

หรือใช้นิพจน์ labda:

(s) => <process the string>

2

ขอบเขต ForEach ทั้งหมด (ฟังก์ชันผู้ได้รับมอบหมาย) ถือเป็นรหัสบรรทัดเดียว (การเรียกใช้ฟังก์ชัน) และคุณไม่สามารถกำหนดจุดพักหรือขั้นตอนในรหัสได้ หากเกิดข้อยกเว้นที่ไม่สามารถจัดการได้บล็อกทั้งหมดจะถูกทำเครื่องหมาย


2

List.ForEach () ได้รับการพิจารณาว่ามีประโยชน์มากกว่า

List.ForEach()พูดในสิ่งที่คุณต้องการ foreach(item in list)ยังระบุอย่างชัดเจนว่าคุณต้องการให้มันทำอย่างไร สิ่งนี้มีList.ForEachอิสระที่จะเปลี่ยนการใช้งานของวิธีส่วนในอนาคต ตัวอย่างเช่น. Net ในอนาคตสมมุติรุ่นอาจทำงานList.ForEachคู่ขนานเสมอภายใต้สมมติฐานที่ว่า ณ จุดนี้ทุกคนมีแกน cpu จำนวนมากซึ่งโดยทั่วไปจะไม่ทำงาน

ในทางกลับกันforeach (item in list)ให้คุณควบคุมวงได้อีกเล็กน้อย ตัวอย่างเช่นคุณรู้ว่ารายการจะได้รับการวนซ้ำในลำดับของการเรียงลำดับและคุณสามารถแบ่งกลางได้อย่างง่ายดายหากรายการตรงตามเงื่อนไข


หมายเหตุล่าสุดเกี่ยวกับปัญหานี้มีให้ที่นี่:

https://stackoverflow.com/a/529197/3043


1

วิธีที่สองที่คุณแสดงใช้วิธีส่วนขยายเพื่อดำเนินการวิธีการมอบหมายสำหรับแต่ละองค์ประกอบในรายการ

วิธีนี้คุณจะมีการเรียกผู้รับมอบสิทธิ์ (= method) อื่น

นอกจากนี้ยังมีความเป็นไปได้ที่จะวนซ้ำรายการด้วยfor for loop


1

สิ่งหนึ่งที่ต้องระวังคือวิธีการออกจากวิธี Generic .ForEach - ดูการสนทนานี้ แม้ว่าลิงค์ดูเหมือนจะบอกว่าวิธีนี้เป็นวิธีที่เร็วที่สุด ไม่แน่ใจว่าทำไม - คุณคิดว่าพวกเขาจะเทียบเท่าเมื่อรวบรวม ...


0

มีวิธีฉันทำมันในแอพของฉัน:

List<string> someList = <some way to init>
someList.ForEach(s => <your actions>);

คุณสามารถใช้เป็นรายการของคุณจาก foreach

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.