2011-01-17 14 views
12

मुझे अक्सर ऐसा कुछ चाहिए:InvalidOperationException से बचने के लिए सबसे अच्छा अभ्यास: संग्रह संशोधित किया गया था?

 foreach (Line line in lines) 
{ 
    if (line.FullfilsCertainConditions()) 
    { 
     lines.Remove(line) 
    } 
} 

यह काम नहीं करता है, क्योंकि मुझे हमेशा InvalidOperationException मिलता है क्योंकि लूप के दौरान गणनाकर्ता बदल दिया गया था।

तो मैंने इस प्रकार के सभी लूप को निम्न में बदल दिया:

List<Line> remove = new List<Line>(); 
foreach (Line line in lines) 
{ 
    if (line.FullfilsCertainConditions()) 
    { 
     remove.Add(line) 
    } 
} 

foreach (Line line in remove) { 
{ 
    lines.Remove(line); 
} 

मुझे यकीन नहीं है कि यह वास्तव में सबसे अच्छा तरीका है क्योंकि सबसे खराब मामले में मुझे मूल पर 2 गुना फिर से करना है सूची और इसलिए इसे एन के बजाय समय 2n की आवश्यकता है।

क्या ऐसा करने का कोई बेहतर तरीका है?

संपादित करें:

मैं मार्क के उत्तर का उपयोग कर ऐसा करने में सक्षम था! लेकिन अगर मेरा संग्रह RemoveAll() लागू नहीं करता है तो क्या होगा?

उदाहरण के लिए

System.Windows.Controls.UIElementCollection

संपादित करें 2:

फिर मार्क की मदद से मैं अब निम्नलिखित कॉल करने में सक्षम हूं सभी ScatterViewItems को हटाएं:

CollectionUtils.RemoveAll(manager.getWindow().IconDisplay.Items, elem => elem.GetType() == typeof(ScatterViewItem)); 
+0

मुझे कुछ समय पहले और कोई समाधान नहीं मिला। यह और भी बदतर है - यह 2 एन नहीं है लेकिन एन^2 'लाइनों के बाद से है। निकालें (रेखा)' संग्रह पर फिर से फिर से शुरू होता है। – Matten

+0

लेकिन ओ (2 एन) ओ (एन) :-) वास्तव में बराबर है: जावा में आप ऐसा करने के लिए एक इटरेटर का उपयोग कर सकते हैं, या कॉपी-ऑन-लिखित संग्रह कार्यान्वयन के लिए जा सकते हैं जो पुनरावृत्ति के दौरान संशोधनों की अनुमति देता है। – Waldheinz

+0

अद्यतन पुनः करें - मेरा संपादन देखें –

उत्तर

17

यह सीधे बेक्ड है List<T> में ly:

lines.RemoveAll(line => line.FullfilsCertainConditions()); 

या सी # में 2.0:

lines.RemoveAll(delegate(Line line) { 
    return line.FullfilsCertainConditions(); 
}); 

गैर List<T> मामले में (सवाल के लिए आपके संपादन), तो आप नीचे की तरह यह कुछ लपेट सकता है (untested) :

static class CollectionUtils 
{ 
    public static void RemoveAll<T>(IList<T> list, Predicate<T> predicate) 
    { 
     int count = list.Count; 
     while (count-- > 0) 
     { 
      if (predicate(list[count])) list.RemoveAt(count); 
     } 
    } 
    public static void RemoveAll(IList list, Predicate<object> predicate) 
    { 
     int count = list.Count; 
     while (count-- > 0) 
     { 
      if (predicate(list[count])) list.RemoveAt(count); 
     } 
    } 
} 

UIElementCollection के बाद से (गैर सामान्य) लागू करता IList यह काम करना चाहिए। और काफी आसानी से, सी # 3.0 के साथ आप IList/IList<T> से पहले जोड़ सकते हैं और इसे एक विस्तार विधि के रूप में जोड़ सकते हैं। एकमात्र सूक्ष्मता यह है कि एनन-विधि का पैरामीटर object होगा, इसलिए आपको इसे दूर करने की आवश्यकता होगी।

+0

क्या .NET 2.0 में इसे प्राप्त करने का कोई तरीका है? – Matten

+2

@ मैटन - एक सी # 2.0 उदाहरण जोड़ा गया। –

+0

@Marc Gravell - बहुत ही सरल, बहुत ही सुरुचिपूर्ण। धन्यवाद :) – Matten

1

आप बस एक फ़िल्टर के साथ मूल सूची की जगह सकता है:

lines = lines.Where(line => line.FullfilsCertainConditions()).ToList(); 
1

एक नई सूची बनाएँ instaed:

public IList<Line> GetListWithoutFullfilsCertainConditions(IList<Line> fullList) 
{ 
    IList<Line> resultList = new List<Line>(fullList.Count); 

    foreach (Line line in fullList) 
    { 
     if (!line.FullfilsCertainConditions()) 
     { 
      resultList.Add(line) 
     } 
    } 

    return resultList; 
} 
+0

डाउन-वोट का कारण, कृपया? –

1

इसके अलावा, आप बस, जबकि पाश का उपयोग कर सकते हैं।

int i = 0; 
while(i < lines.Count) 
{ 
    if (lines[i].FullfilsCertainConditions()) 
    { 
    lines.RemoveAt(i); 
    } 
    else {i++;} 
} 
संबंधित मुद्दे

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