الفرق بين حلقات foreach وfor عبر فئة IEnumerable في C#

StackOverflow https://stackoverflow.com/questions/44220

  •  09-06-2019
  •  | 
  •  

سؤال

لقد قيل لي أن هناك اختلافًا في الأداء بين كتل التعليمات البرمجية التالية.

foreach (Entity e in entityList)
{
 ....
}

و

for (int i=0; i<entityList.Count; i++)
{
   Entity e = (Entity)entityList[i];
   ...
}

أين

List<Entity> entityList;

لا أتوقع CLR ولكن مما يمكنني قوله أنه يجب عليهم أن يتلخصوا في نفس الكود بشكل أساسي.هل لدى أي شخص دليل ملموس (سأأخذ الأوساخ المعبأة) بطريقة أو بأخرى؟

هل كانت مفيدة؟

المحلول

يقوم foreach بإنشاء مثيل للعداد (الذي تم إرجاعه من GetEnumerator) ويحتفظ هذا العداد أيضًا بالحالة طوال دورة حلقة foreach.ثم يقوم بعد ذلك باستدعاء كائن Next() بشكل متكرر على العداد ويقوم بتشغيل التعليمات البرمجية الخاصة بك لكل كائن يقوم بإرجاعه.

إنها لا تتلخص في نفس الكود بأي شكل من الأشكال، حقًا، وهو ما ستراه إذا كتبت العداد الخاص بك.

نصائح أخرى

هنا مقالة جيدة توضح اختلافات IL بين الحلقتين.

يعد Foreach أبطأ من الناحية الفنية ولكنه أسهل في الاستخدام وأسهل في القراءة.ما لم يكن الأداء أمرًا بالغ الأهمية، فأنا أفضل حلقة foreach على حلقة for.

تتوافق عينة foreach تقريبًا مع هذا الرمز:

using(IEnumerator<Entity> e = entityList.GetEnumerator()) {
    while(e.MoveNext()) {
        Entity entity = e.Current;
        ...
    }
}

توجد هنا تكاليفان لا يتعين على حلقة for العادية دفعهما:

  1. تكلفة تخصيص كائن العداد بواسطة الكيانList.GetEnumerator().
  2. تكلفة استدعاء الطريقتين الظاهريتين (MoveNext وCurrent) لكل عنصر من عناصر القائمة.

نقطة واحدة غابت هنا:تحتوي القائمة على خاصية Count، وهي تقوم داخليًا بتتبع عدد العناصر الموجودة فيها.

IEnumerable لا يفعل ذلك.

إذا قمت بالبرمجة على الواجهة IEnumerable واستخدمت طريقة تمديد العدد، فسيتم تعداد العناصر فقط.

هناك نقطة خلافية لأنه في IEnumerable لا يمكنك الرجوع إلى العناصر حسب الفهرس.

لذا، إذا كنت تريد القفل على القوائم والمصفوفات، فيمكنك الحصول على زيادات طفيفة في الأداء.

إذا كنت تريد استخدام المرونة foreach والبرنامج إلى IEnumerable.(مما يسمح باستخدام linq و/أو عائد العائد).

ومن حيث المخصصات، سيكون من الأفضل أن ننظر إليها هذه المدونة.يُظهر بالضبط الظروف التي يتم فيها تخصيص العداد في الكومة.

أعتقد أن أحد المواقف المحتملة حيث أنت قد الحصول على مكاسب في الأداء هو إذا كان حجم النوع القابل للإحصاء وحالة الحلقة ثابتًا؛على سبيل المثال:

const int ArraySize = 10;
int[] values = new int[ArraySize];

//...

for (int i = 0; i 

في هذه الحالة، اعتمادًا على مدى تعقيد نص الحلقة، قد يتمكن المترجم من استبدال الحلقة باستدعاءات مضمنة.ليس لدي أي فكرة عما إذا كان برنامج التحويل البرمجي .NET يقوم بذلك، ويكون ذو فائدة محدودة إذا كان حجم النوع القابل للتعداد ديناميكيًا.

حالة واحدة حيث foreach قد يكون الأداء أفضل مع هياكل البيانات مثل القائمة المرتبطة حيث يعني الوصول العشوائي اجتياز القائمة؛العداد الذي يستخدمه foreach من المحتمل أن يقوم بتكرار عنصر واحد في كل مرة، مما يجعل كل وصول O(1) والحلقة الكاملة O(n)، ولكن استدعاء المفهرس يعني البدء من الرأس والعثور على العنصر في الفهرس الصحيح؛O(N) كل حلقة لـ O(n^2).

Personally I don't usually worry about it and use foreach any time I need all items and don't care about the index of the item. If I'm not working with all of the items or I really need to know the index, I use for.المرة الوحيدة التي رأيت فيها أن الأمر يشكل مصدر قلق كبير هو الهياكل مثل القوائم المرتبطة.

For Loop
for loop is used to perform the opreration n times
for(int i=0;i<n;i++)
{
l=i;
}
foreach loop

int[] i={1,2,3,4,5,6}
foreach loop is used to perform each operation value/object in IEnumarable 
foreach(var k in i)
{
l=k;
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top