تكرّر حلقة for كتلة ما دام الشرط صحيحًا، مع كتابة التهيئة والخطوة في ترويسة الحلقة. هي الحلقة التي تستخدمها حين تعدّ: عدد ثابت من التكرارات، أو كل فهرس في مصفوفة.
المخرجات:
Lap 1 of 5
Lap 2 of 5
Lap 3 of 5
Lap 4 of 5
Lap 5 of 5
كيف تُنفَّذ الأجزاء الثلاثة
تُنفَّذ for (initializer; condition; iterator) بترتيب ثابت:
- يُنفَّذ المهيّئ (
int i = 1) مرة واحدة، قبل أي شيء آخر. - يُفحص الشرط (
i <= 5). إن كان false تنتهي الحلقة ولا يُنفَّذ الجسم مرة أخرى. - يُنفَّذ الجسم.
- يُنفَّذ المكرِّر (
i++)، ثم يعود التحكم إلى الخطوة 2.
ولأن الشرط يُفحص قبل كل تكرار، يمكن لحلقة for أن تعمل صفر مرات: لا تدخل for (int i = 10; i < 5; i++) جسمها أبدًا. تتصرف الحلقة أعلاه مثل حلقة while هذه:
int i = 1;
while (i <= 5)
{
Console.WriteLine($"Lap {i} of 5");
i++;
}
يختلف الشكلان في تفصيل واحد: continue داخل جسم for ما زالت تنفّذ المكرِّر، بينما في نسخة while هذه ستقفز فوق i++، فتختبر الحلقة i نفسها مجددًا وقد تدور إلى الأبد. المتغيّر المصرّح به في المهيّئ لا يوجد إلا داخل الحلقة. إن احتجت إلى قيمته الأخيرة بعدها فصرّح به قبل الحلقة: int i; for (i = 0; ...; i++) { }.
العدّ التنازلي والقفز بخطوات
يمكن للمكرِّر أن يكون أي تعبير. أنقص للعدّ التنازلي، أو أضف أكثر من واحد للقفز، أو اضرب لمتتالية هندسية:
المخرجات:
3... 2... 1... liftoff
00 15 30 45
1 10 100 1000
تأكّد أن المكرِّر يتحرك نحو جعل الشرط false. تعدّ for (int i = 0; i < 10; i--) بعيدًا عن 10 ولا تتوقف إلا حين تلتفّ i بعد ملياري تكرار.
المرور على مصفوفة بالفهرس
تكشف المصفوفات Length، والقوائم Count، والفهارس الصالحة من 0 حتى أقل منها بواحد. يمنحك الفهرس ما لا تمنحه foreach: الوصول إلى الموضع والجيران وإمكانية الكتابة في العنصر.
المخرجات:
#1: 17.99
#2: 4.50
#3: 38.25
#4: 7.43
item 1 vs item 0: down
item 2 vs item 1: up
item 3 vs item 2: down
تبدأ الحلقة الأخيرة من 1 لأنها تقارن كل عنصر بالذي قبله. تغطي صفحة المصفوفات إنشاء المصفوفات نفسها وفرزها ونسخها.
عدة متغيّرات للحلقة
يمكن للمهيّئ أن يصرّح بعدة متغيّرات من النوع نفسه، ويمكن للمكرِّر أن يحدّث عدة متغيّرات، مفصولة بفواصل. الاستخدام التقليدي فهرسان يسيران أحدهما نحو الآخر:
المخرجات:
racecar is a palindrome: True
9, 1, 8, 3
يجب أن يشترك المتغيّران في نوع واحد في تصريح واحد: int left = 0, right = 10 تعمل، وint i = 0, string s = "" لا تعمل.
الحلقات المتداخلة
الحلقة داخل حلقة تنفّذ نطاقها كله مرة لكل تكرار من الحلقة الخارجية. الجداول والشبكات هي الحالة النموذجية:
المخرجات:
1 2 3 4
2 4 6 8
3 6 9 12
4 8 12 16
يُنفَّذ الجسم الداخلي 16 مرة (4 × 4). التداخل يضاعف العمل بسرعة: حلقتان متداخلتان على 10,000 عنصر لكل منهما تعني 100 مليون تكرار. وbreak في الحلقة الداخلية لا تغادر إلا الحلقة الداخلية؛ وتُظهر صفحة break وcontinue كيف تغادر الاثنتين.
الحلقات اللانهائية: for(;;)
الأجزاء الثلاثة في الترويسة كلها اختيارية. دون شرط تعمل الحلقة حتى يغادرها شيء من داخلها:
المخرجات:
Month 1: 70 left
Month 2: 40 left
Month 3: 10 left
Overdrawn in month 4: -20
تُترجم for (;;) وwhile (true) إلى الشيء نفسه؛ اختر ما تستخدمه قاعدة شيفرتك.
أخطاء الإزاحة بواحد
أشيع خطأ في for شرط يتجاوز بخطوة واحدة:
المخرجات:
Ada
Grace
Linus
IndexOutOfRangeException at index 3
لمصفوفة من 3 عناصر الفهارس 0 و1 و2، فالشرط الصحيح i < names.Length. والخطأ المقابل هو العدّ التنازلي بـ i > 0، الذي لا يزور الفهرس 0 أبدًا. وعلى List<T> يرمي الخطأ نفسه ArgumentOutOfRangeException بدلًا من ذلك.
حذف العناصر أثناء المرور
الحذف من List<T> داخل حلقة تسير للأمام يزيح كل عنصر لاحق موضعًا واحدًا للخلف، فتتخطّى الحلقة العنصر الذي يلي كل حذف مباشرة:
المخرجات:
forward: 5, 0, 3, 7
backward: 5, 3, 7
تركت الحلقة الأمامية 0 خلفها: بعد حذف الفهرس 1 انتقل الصفر الثاني إلى الفهرس 1، لكن i++ كانت قد انتقلت إلى الفهرس 2. المرور المعكوس يتجنّب ذلك لأن الحذف لا يزيح إلا عناصر زرتها أصلًا. وكبديل في سطر واحد تفعل stock.RemoveAll(x => x == 0) الشيء نفسه. أما حلقة foreach فلا تسمح بالحذف إطلاقًا؛ ترمي InvalidOperationException.
العدّادات العشرية
تجنّب عدّادات الحلقات من نوع double التي تختبر المساواة. ليس لـ 0.1 تمثيل ثنائي دقيق، فإضافتها عشر مرات لا تنتج 1.0 بالضبط:
for (double x = 0; x != 1.0; x += 0.1) // never ends: x skips past 1.0
{
}
عدّ بعدد صحيح واحسب القيمة منه بدلًا من ذلك: for (int i = 0; i <= 10; i++) { double x = i / 10.0; }.
for مقابل foreach
| الحاجة | الاستخدام |
|---|---|
| قراءة كل عنصر بالترتيب | foreach |
الفهرس، أو الجيران (i - 1 وi + 1) | for |
| الإسناد إلى عنصر في مصفوفة | for |
| القفز بخطوتين، المرور المعكوس | for |
الحذف من List أثناء المرور | for معكوسة |
مجموعة بلا مفهرس (HashSet وDictionary ونتائج LINQ) | foreach |
| تكرار شيء N مرة، دون مجموعة | for |
على المصفوفات تُترجم الحلقتان إلى شيفرة متطابقة تقريبًا، فاختر بحسب سهولة القراءة لا السرعة.
الأسئلة الشائعة
ما صياغة حلقة for في C#؟
for (initializer; condition; iterator) { body }، مثل for (int i = 0; i < 5; i++) { ... }. يُنفَّذ المهيّئ مرة واحدة، ويُفحص الشرط قبل كل تكرار، ويُنفَّذ المكرِّر بعد كل تكرار. تنتهي الحلقة أول مرة يكون فيها الشرط false.
كيف أمرّ بحلقة معكوسة في C#؟
ابدأ من الفهرس الأخير وأنقص: for (int i = items.Length - 1; i >= 0; i--). لاحظ >= 0 لا > 0، وإلا تُتخطّى العنصر الأول. والمرور المعكوس هو أيضًا الطريقة الآمنة لحذف عناصر من List أثناء المرور.
متى أستخدم for بدل foreach في C#؟
استخدم for حين تحتاج إلى الفهرس (لمقارنة الجيران، أو الكتابة في مصفوفة، أو القفز بأكثر من خطوة)، وحين تعدّ بدل أن تمرّ على مجموعة، وحين تحذف عناصر من قائمة. واستخدم foreach لقراءة كل عنصر بالترتيب؛ فهي أقصر ولا فهرس فيها قد تخطئ فيه.
كيف أكتب حلقة لا نهائية في C#؟
for (;;) { ... } أو while (true) { ... }. الأجزاء الثلاثة في ترويسة for كلها اختيارية، والشرط الغائب يُحسب true. غادر الحلقة بـ break أو return حين يتحقق شرط الخروج.
لماذا أحصل على IndexOutOfRangeException في حلقة for؟
الشرط يسمح للفهرس ببلوغ الطول: تزور i <= array.Length العنصر array[array.Length]، وهو بعد العنصر الأخير بواحد. الفهارس الصالحة من 0 إلى Length - 1، فيجب أن يكون الشرط i < array.Length (أو i < list.Count لـ List، التي ترمي ArgumentOutOfRangeException بدلًا من ذلك).