2012-07-19 14 views
7

मैं एक LINQ प्रश्न हैं की कोशिश कर मदद, परिणाम जिनमें से मैं एक foreach पाश में पुनरावृति।foreach और LINQ क्वेरी - जरूरत कृपया समझने की

पहले एक प्रश्न जो एक मेज लेआउट पैनल से नियंत्रण का एक संग्रह पकड़ लेता है, मैं तो संग्रह से अधिक पुनरावृति और इस तरह TableLayoutPanel से नियंत्रण हटाने के लिए:

var AllItems = (from Item in this.tableLayoutPanel1.Controls.OfType<ItemControl>() 
       select Item); 

foreach (ItemControl item in AllItems) 
{ 
    Trace.WriteLine("Removing " + item.ToString()); 
    this.tableLayoutPanel1.Controls.Remove(item); 
    item.Dispose(); 
} 

ऊपर मैं के रूप में काम नहीं करता अपेक्षित (यानी एक त्रुटि फेंक दें), यह केवल आधे नियंत्रण (ओडीडी क्रमांकित वाले) को हटा देता है ऐसा प्रतीत होता है कि प्रत्येक पुनरावृत्ति पर ऑलइटम अपने स्वयं को कम कर देता है, और हालांकि अंतर्निहित संग्रह को संशोधित किया जा रहा है, कोई त्रुटि नहीं फेंक दी गई है।

अगर मैं तार की एक सरणी के साथ समान है:

 string[] strs = { "d", "c", "A", "b" }; 
     List<string> stringList = strs.ToList(); 

     var allitems = from letter in stringList 
         select letter; 

     foreach (string let in allitems) 
     { 
      stringList.Remove(let); 

     } 

इस बार दृश्य स्टूडियो (उम्मीद के रूप में) में शिकायत कि अंतर्निहित संग्रह बदल गया है एक त्रुटि फेंकता है।

क्यों पहला उदाहरण भी उड़ा नहीं करता है?

वहाँ Iterators/IEnumerable के बारे में कुछ है कि मैं यहाँ समझ नहीं कर रहा हूँ और मुझे आश्चर्य है अगर किसी ने मुझे समझने की क्या LINQ और foreach के साथ हुड के नीचे चल रहा है मदद कर सकता है।

(मुझे पता है कि मैं AllItems.ToList() द्वारा दोनों मुद्दों इलाज कर सकते हैं हूँ, इससे पहले कि मैं पुनरावृति, लेकिन समझने के लिए क्यों दूसरे उदाहरण एक त्रुटि फेंकता चाहते हैं और पहली नहीं है)

उत्तर

10

क्यों क्या पहला उदाहरण भी उड़ा नहीं है?

tableLayoutPanel1.Controls भीतर IEnumerator कार्यान्वयन उचित चेकों प्रदर्शन देखने के लिए अगर संग्रह संशोधित किया गया है नहीं है। इसका मतलब यह नहीं है कि यह एक सुरक्षित संचालन है (क्योंकि परिणाम उचित नहीं हैं), बल्कि यह कि कक्षा को ऐसे तरीके से कार्यान्वित नहीं किया गया था जो अपवाद उठाता है (जैसा कि शायद यह होना चाहिए)।

जब आप List<T> के माध्यम से गणना करते हैं तो उठाया गया अपवाद और एक आइटम को हटाकर वास्तव में List<T>.Enumerator प्रकार द्वारा उठाया जाता है, और यह "सामान्य उद्देश्य" भाषा सुविधा नहीं है।

ध्यान दें कि यह एक अच्छा सुविधा List<T>.Enumerator प्रकार के है IEnumerator<T> प्रलेखन के रूप में स्पष्ट रूप से कहा:

एक प्रगणक तक संग्रह अपरिवर्तित रहता है के रूप में मान्य रहता है। यदि संग्रह में परिवर्तन किए जाते हैं, जैसे तत्व जोड़ना, संशोधित करना या हटाना, तो गणनाकर्ता को अपरिवर्तनीय रूप से अमान्य कर दिया गया है और इसका व्यवहार अपरिभाषित है।

हालांकि किसी अनुबंध को फेंकने के अपवाद की आवश्यकता नहीं है, तो जब आप "अपरिभाषित व्यवहार" के क्षेत्र में प्रवेश कर रहे हैं तो ऐसा करने के लिए कार्यान्वयन के लिए फायदेमंद है।

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

+0

तथ्य से संबंधित नहीं इस समस्या के लिए कारण यह है कि 'Enumerable.OfType' आस्थगित निष्पादन का उपयोग करके लागू किया गया है है, इसलिए अनुक्रम हमेशा हर गणन पर परिवर्तन? –

+2

@TimSchmelter Well, 'OfType ' केवल स्रोत गणनाकर्ता का उपयोग करके गणना करता है - जबकि यह जांच नहीं कर रहा है, वही बात तब होगी जब आप ऑब्जेक्ट्स की गणना करते हैं, और लूप के अंदर चेक डालते हैं। अंतर्निहित गणनाकर्ता यह जांच नहीं कर रहा है कि नियंत्रण संग्रह संशोधित है या नहीं। –

+0

@TimSchmelter: मुझे संदेह है कि सभी LINQ संचालन मूल पोस्ट कोड से हटा दिए जा सकते हैं, और यह वैसे ही व्यवहार करेगा। जैसा कि रीड कहते हैं, यह गणनाकर्ता कार्यान्वयन का परिणाम है। ऑफ टाइप के पास इसके साथ कुछ लेना देना नहीं है। – StriplingWarrior

2

तो मेरे पहले मामले में लिनक्स अभिव्यक्ति का पुन: मूल्यांकन किया जा रहा है प्रत्येक फ़ोरैच लूप का पुनरावृत्ति? या linq IENumerator tablelayoutpanel.controls संग्रह से प्राप्त कर रहा है? इसके बारे में

लगता है कि अगर यह इस है जो दोनों अपने मामलों में हो रहा है की तरह हो रहा गया:

foreach (string l in (from letter in stringList select letter)) 
    { 
     stringList.Remove(l); 
    } 

ऊपर छद्म कोड दिखाता है कि के रूप में आप किसी आइटम को निकालने की कोशिश करते हैं आप में अनिवार्य रूप से कर रहे हैं stringList संग्रह की गणना के बीच। रीड कह रहा है कि दूसरे मामले में stringList का गणक यह सुनिश्चित कर रहा है कि कोई भी इसके साथ गड़बड़ कर रहा है, जबकि यह गणना के बीच में है।

वह कह रहा है कि आपके नियंत्रण के मामले में, नियंत्रण संख्याकर्ता खुशी से साथ जा रहा है और यह देखने के लिए जांच नहीं कर रहा है कि कोई भी अपने डेटा के साथ बंदरगाह कर रहा है या नहीं।

letters के लिए यह प्रश्न हर बार letters छुआ है जो समय हम foreach पाश में हैं शामिल revaluated किया जाएगा::

IEnumerable<string> letters = from letter in stringList select letter); 

    // everytime we hit this we're going to hit the stringList collection 
    foreach (string l in letters) 
    { 
     stringList.Remove(l); 
    } 

इस क्वेरी का उपयोग करता

पूर्णता के लिए, इन दो उदाहरणों की तुलना ToList() जो तुरंत letters में भरता है और हम अब फ़ोरैच लूप में stringList संग्रह को स्पर्श नहीं करेंगे।

List<string> letters = (from letter in stringList select letter).ToList() 

    // because we ran ToList(), we will no longer enumerate stringList 
    foreach (string l in letters) 
    { 
     stringList.Remove(l); 
    } 
+0

धन्यवाद ब्रैड। अब मैं समझ गया। @ हर कोई जिसने जवाब दिया: धन्यवाद, इसने वास्तव में मेरे दिमाग में इस विषय को मंजूरी दे दी है। तुम लोग कमाल के हो! – John

0

इस प्रयास करें:

var AllItems = (from Item in this.tableLayoutPanel1.Controls.OfType<ItemControl>() 
       select Item); 


    int totalCount = AllItems .Count; 
    for (int i = 0; i < totalCount; i++) 
     { 
      AllItems .RemoveAt(0); 
     } 
संबंधित मुद्दे