"تم تعديل المجموعة الغريبة بعد أن تم استثناء العداد" استثناء "

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

  •  20-08-2019
  •  | 
  •  

سؤال

ربما يمكن لأي شخص أن يوجهني في الاتجاه الصحيح ، لأنني متعثر تمامًا على هذا.

لدي وظيفة تقوم ببساطة بطباعة قائمة مرتبطة بالفصول:

    LinkedList<Component> components = new LinkedList<Component>();
    ...
    private void PrintComponentList()
    {
        Console.WriteLine("---Component List: " + components.Count + " entries---");
        foreach (Component c in components)
        {
            Console.WriteLine(c);
        }
        Console.WriteLine("------");
    }

ال Component الكائن لديه في الواقع العرف ToString() استدعاء على هذا النحو:

    int Id;
    ...
    public override String ToString()
    {
        return GetType() + ": " + Id;
    }

تعمل هذه الوظيفة عادةً بشكل جيد - ومع ذلك ، فقد واجهت المشكلة أنه عندما تبني ما يقرب من 30 إدخالات أو نحو ذلك في القائمة ، PrintcomplentList foreach يعود البيان مع InvalidOperationException: Collection was modified after the enumerator was instantiated.

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

أنا متعثر تمامًا ، هل واجه أي شخص آخر هذا؟

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

المحلول

أظن أن المكان المناسب للبدء في البحث سيكون في أي أماكن تقوم فيها بمعالجة القائمة - أي إدراج/إزالة/إعادة تعيين العناصر. شكوكي هي أنه سيكون هناك رد اتصال/معالج حتى مكان ما يتم إطلاقه بشكل غير متزامن (ربما كجزء من XNA رسم حلقات الخ) ، والتي تقوم بتحرير القائمة - مما يسبب هذه المشكلة بشكل أساسي كشرط سباق.

للتحقق مما إذا كان هذا هو الحال ، ضع بعض الإخراج Debug/Trace حول الأماكن التي تتلاعب بالقائمة ، ومعرفة ما إذا كان ذلك (وعلى وجه الخصوص ، قبل الاستثناء) يدير رمز التلاعب في نفس الوقت الذي يخرج فيه وحدة التحكم:

private void SomeCallback()
{
   Console.WriteLine("---Adding foo"); // temp investigation code; remove
   components.AddLast(foo);
   Console.WriteLine("---Added foo"); // temp investigation code; remove
}

لسوء الحظ ، غالبًا ما تكون مثل هذه الأشياء ألمًا للتصحيح ، حيث أن تغيير الكود للتحقيق فيه غالبًا ما يغير المشكلة (أ Heisenbug).

إجابة واحدة هي مزامنة الوصول ؛ أي في الكل الأماكن التي تعدل القائمة ، استخدم أ lock حول العملية الكاملة:

LinkedList<Component> components = new LinkedList<Component>();
readonly object syncLock = new object();
...
private void PrintComponentList()
{
    lock(syncLock)
    { // take lock before first use (.Count), covering the foreach
        Console.WriteLine("---Component List: " + components.Count
              + " entries---");
        foreach (Component c in components)
        {
           Console.WriteLine(c);
        }
        Console.WriteLine("------");
    } // release lock
}

وفي رد الاتصال الخاص بك (أو أيا كان)

private void SomeCallback()
{
   lock(syncLock)
   {
       components.AddLast(foo);
   }
}

على وجه الخصوص ، قد تتضمن "العملية الكاملة":

  • تحقق من العد و foreach/for
  • تحقق من وجود و أدخل/إزالة
  • إلخ

(أي ليس العمليات الفردية/المنفصلة - ولكن وحدات العمل)

نصائح أخرى

بدلاً من foreach, ، أنا أستعمل while( collection.count >0) ثم استخدام collection[i].

لا أعرف ما إذا كان هذا مناسبًا لـ OP ولكن كان لدي نفس الخطأ ووجدت هذا الموضوع أثناء بحث Google. تمكنت من حلها عن طريق إضافة استراحة بعد إزالة عنصر في الحلقة.

foreach( Weapon activeWeapon in activeWeapons ){

            if (activeWeapon.position.Z < activeWeapon.range)
            {
                activeWeapons.Remove(activeWeapon);
                break; // Fixes error
            }
            else
            {
                activeWeapon.position += activeWeapon.velocity;
            }
        }
    }

إذا تركت الاستراحة ، فستحصل على الخطأ "InvalidOperationException: تم تعديل المجموعة بعد أن تم إنشاء إنشاء العداد."

استخدام Break يمكن أن يكون وسيلة ولكن قد يؤثر على سلسلة عمليتك. ما أفعله في هذه الحالة في تحويل ببساطة foreach إلى التقليدية for عقدة

for(i=0; i < List.count; i++)
{
    List.Remove();
    i--;
}

هذا يعمل دون أي مشاكل.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top