2011-06-04 13 views
7

मैंने Queues<T> का उपयोग किसी भी वास्तविक डिग्री से पहले नहीं किया है, इसलिए मुझे कुछ स्पष्ट याद आ रही है। मैं इस तरह एक Queue<EnemyUserControl> (हर फ्रेम) के माध्यम से पुनरावृति करने के लिए कोशिश कर रहा हूँ:कतार फॉरएच लूप फेंक अमान्यऑपरेशन अपवाद

foreach (var e in qEnemy) 
{ 
    //enemy AI code 
} 

जब एक दुश्मन मर जाता है, दुश्मन उपयोगकर्ता नियंत्रण एक घटना मैंने सदस्यता ली है को जन्म देती है और मैं इस (पहले दुश्मन करना कतार डिजाइन द्वारा हटा दिया जाता):

void Enemy_Killed(object sender, EventArgs e) 
{  
    qEnemy.Dequeue(); 

    //Added TrimExcess to check if the error was caused by NULL values in the Queue (it wasn't :)) 
    qEnemy.TrimExcess(); 
} 

हालांकि, बाद विपंक्ति विधि कहा जाता है, मैं foreach पाश पर एक InvalidOperationException मिलता है। जब मैं Peek का उपयोग करता हूं, तो इसमें कोई त्रुटि नहीं होती है, इसलिए इसे क्यूई को बदलने के साथ कुछ करना पड़ता है क्योंकि डेक्यू ऑब्जेक्ट को हटा देता है। मेरा प्रारंभिक अनुमान यह है कि यह शिकायत कर रहा है कि मैं एक संग्रह को संशोधित कर रहा हूं जिसे गणनाकर्ता द्वारा पुनरावृत्त किया जा रहा है, लेकिन लूप के बाहर डेक्यूइंग किया जा रहा है?

कोई भी विचार जो इस मुद्दे को उत्पन्न कर सकता है?

धन्यवाद

+1

आप का उपयोग करना चाहिए 'जबकि (queue.Any()) queue.Dequeue();' – Telemat

उत्तर

16

आप foreach लूप के अंदर कतार संशोधित कर रहे हैं। यह अपवाद का कारण बनता है।
सरलीकृत कोड मुद्दा प्रदर्शित करने के लिए:

var queue = new Queue<int>(); 
queue.Enqueue(1); 
queue.Enqueue(2); 

foreach (var i in queue) 
{ 
    queue.Dequeue(); 
} 

संभव समाधान ToList() जोड़ने के लिए, इस तरह है:

foreach (var i in queue.ToList()) 
{ 
    queue.Dequeue(); 
} 
+0

डी 'ओह, एक चेहरे की पल का थोड़ा सा। एआई कोड में से एक विधि 'मूवमेंट' विधि को कॉल करती है, जो बदले में, मारे गए घटना को उठाती है (मैंने सोचा था कि इसे लूप के बाहर कुछ कोड द्वारा उठाया गया था), इसलिए लूप के भीतर डेक्यू किया जाता है। 'ToList() 'विधि पूरी तरह से काम करती है। धन्यवाद! – keyboardP

1

यह गणक के सामान्य व्यवहार है। अधिकांश गणककों को केवल तभी कार्य करने के लिए डिज़ाइन किया गया है जब अंतर्निहित संग्रह स्थिर रहता है। यदि संग्रह को गणना करते समय संग्रह बदल जाता है तो MoveNext पर अगली कॉल, जिसे foreach ब्लॉक द्वारा आपके लिए इंजेक्शन दिया गया है, यह अपवाद उत्पन्न करेगा।

Dequeue आपरेशन स्पष्ट रूप से संग्रह बदलता है और कि क्या समस्या का कारण है है। वर्कअराउंड प्रत्येक संग्रह को लक्षित संग्रह से दूसरे संग्रह में निकालना चाहते हैं। लूप पूरा होने के बाद आप दूसरे संग्रह के माध्यम से चक्र चला सकते हैं और लक्ष्य से हटा सकते हैं।

हालांकि, यह कम से कम थोड़ा अजीब हो सकता है, क्योंकि Dequeue ऑपरेशन केवल अगले आइटम को हटा देता है। आपको एक अलग संग्रह प्रकार पर स्विच करना पड़ सकता है जो मनमाने ढंग से हटाने की अनुमति देता है।

यदि आप Queue के साथ रहना चाहते हैं तो आपको प्रत्येक आइटम को खाली करने के लिए मजबूर होना होगा और उन वस्तुओं को सशर्त रूप से दोबारा कतारबद्ध करना होगा जिन्हें हटाया नहीं जाना चाहिए। फिर भी क्यूइंग से निकलने के लिए ठीक होने वाले सामानों का ट्रैक रखने के लिए आपको दूसरे संग्रह की आवश्यकता होगी।

0

आप उन पर पुनरावृत्ति करते हुए एक संग्रह से तत्व नहीं निकाल सकते।

सबसे अच्छा समाधान मैंने पाया एक "सूची <> toDelete" का उपयोग करें और जोड़ने के जो भी आप उस सूची को निकालना चाहते हैं के लिए है।एक बार जब foreach पाश समाप्त होता है, यदि आप ऐसा तरह toDelete सूची में संदर्भों का उपयोग लक्ष्य संग्रह से तत्वों को दूर कर सकते हैं:

foreach (var e in toDelete) 
    target.Remove(e); 
toDelete.Clear(); 

अब के बाद से यह एक पंक्ति है, तो आप बस समय की संख्या गिनती करने के लिए सक्षम हो सकता है आप एक पूर्णांक में डेक्यू करना चाहते हैं और बाद में उन्हें निष्पादित करने के लिए लूप के लिए एक सरल उपयोग करना चाहते हैं (मेरे पास इस संबंध में कतारों के साथ इतना अधिक अनुभव नहीं है)।

+0

आप बस कतार पर फिर से शुरू कर सकते हैं और इसे साफ़ कर सकते हैं। उस स्थिति में प्रभाव एक सूची का उपयोग करने जैसा ही है, इसलिए इसके लिए कतार का उपयोग करने में वास्तव में कोई बिंदु नहीं है (यदि आपको व्यक्तिगत रूप से डेक्यू करने की आवश्यकता नहीं है)। – arni

0

इससे कोई फ़र्क नहीं पड़ता कि आप संग्रह को संशोधित कर रहे हैं। यदि आप अपने सदस्यों की गणना करते समय एक संग्रह संशोधित किया जाता है, तो आपको अपवाद मिलता है। आप ताले का उपयोग कर सकते हैं और सुनिश्चित कर सकते हैं कि संग्रह को संशोधित करते समय संशोधित नहीं किया गया है या यदि आप .NET 4.0 का उपयोग कर रहे हैं QueueConcurrentQueue के साथ प्रतिस्थापित करें।

15

मैं जानता हूँ कि यह एक पुरानी पोस्ट है लेकिन क्या निम्नलिखित के बारे में:

var queue = new Queue<int>(); 
queue.Enqueue(1); 
queue.Enqueue(2); 

do { 
    var val = queue.Dequeue(); 
} 
while (queue.Count > 0); 

चीयर्स

+4

मैं थोड़ी देर के लिए इसे थोड़ी देर में बदलने/करने की बजाय सिफारिश करता हूं, ताकि आप पहले प्रदर्शन करने से पहले काउंटर चेक कर सकें। क्यूई खाली होने पर। – DaveD

संबंधित मुद्दे