مقدمة: تعرف على هذا الخطأ الذي كسر الكود ملايين المرات
هل سبق لك أن كتبت حلقة foreach بسيطة، وقمت بتشغيل البرنامج، وفجأة - بام! - يظهر استثناء InvalidOperationException برسالة مخيفة: "Collection was modified; enumeration operation may not execute"؟ إذا كان الأمر كذلك، فأنت لست وحدك. هذا السؤال على Stack Overflow حصد آلاف المشاهدات، ويطرحه كل من المبتدئين والمطورين ذوي الخبرة.
في هذه المقالة، سنشرح لماذا يحدث هذا الخطأ، وكيفية إعادة إنتاجه، والأهم من ذلك - كيفية تجنبه. سنستعرض ثلاث طرق مثبتة، ونكتب كودًا حقيقيًا بلغة C#، ونقدم لك خطة عمل واضحة. هيا بنا!
ما هو "Collection was modified"؟ تحليل الخطأ
الخطأ Collection was modified; enumeration operation may not execute هو استثناء من نوع InvalidOperationException، يتم طرحه عندما تحاول تعديل مجموعة (إضافة أو حذف أو استبدال عنصر) أثناء تكرارها باستخدام foreach أو أي عداد آخر (IEnumerator).
تخيل أنك تقرأ كتابًا، وفي نفس الوقت يقوم شخص آخر بتمزيق صفحات منه. ستفقد التركيز ولن تعرف ماذا تقرأ بعد ذلك. هذا ما يحدث للعداد: فهو لا يعرف كيف يستمر إذا تغيرت المجموعة أثناء السير.
كيف يبدو الكود الذي يسبب الخطأ؟
إليك مثالًا كلاسيكيًا سيؤدي بالتأكيد إلى الخطأ:
List<string> fruits = new List<string> { "apple", "banana", "cherry", "date" };
foreach (var fruit in fruits){ if (fruit == "banana") { fruits.Remove(fruit); // خطأ! تم تعديل المجموعة }}شغّل هذا الكود - وستحصل على InvalidOperationException في التكرار الثاني. لماذا؟ لأن foreach يستخدم عدادًا داخليًا يمنع تعديل المجموعة أثناء المرور عليها.
لماذا يحدث هذا؟ نظرة تحت غطاء C#
عندما تكتب foreach، يقوم مترجم C# بتحويله إلى استدعاء للدالة GetEnumerator(). هذه الدالة ترجع كائنًا من نوع IEnumerator يخزن حالة المرور (الفهرس الحالي، إصدار المجموعة). في كل مرة يتم فيها تعديل المجموعة (عبر Add، Remove، Clear، إلخ)، يزداد عداد الإصدار الداخلي لها. يتحقق العداد من هذا الإصدار في كل خطوة. إذا لم يتطابق - يتم طرح الاستثناء.
تم تصميم هذا للحماية من السلوك غير المحدد. تخيل أنك تحذف عنصرًا من منتصف القائمة: ستتغير فهارس جميع العناصر التالية، وقد يتخطى العداد عنصرًا أو يعالج نفس العنصر مرتين. قرر مطورو .NET أنه من الأفضل طرح استثناء بدلاً من السماح بخطأ منطقي غير ملحوظ.
أفضل 3 طرق لتجنب خطأ "Collection was modified"
الآن بعد أن عرفنا العدو، دعنا نرى كيف نهزمه. هناك ثلاث طرق رئيسية، وكل منها مناسب لمواقف مختلفة.
الطريقة 1: نسخ المجموعة قبل التكرار
أبسط وأكثر الطرق أمانًا - إنشاء نسخة من المجموعة والتكرار عليها، مع إجراء التعديلات على الأصل.
List<string> fruits = new List<string> { "apple", "banana", "cherry", "date" };
// إنشاء نسخة من القائمة للتكرارforeach (var fruit in fruits.ToList()) // أو new List<string>(fruits){ if (fruit == "banana") { fruits.Remove(fruit); // الآن هذا آمن }}
Console.WriteLine(string.Join(", ", fruits)); // الإخراج: apple, cherry, dateالإيجابيات: البساطة، سهولة القراءة، مناسبة للمجموعات الصغيرة.
السلبيات: تتطلب ذاكرة إضافية، قد تكون بطيئة للقوائم الكبيرة.
نصيحة: مي