2012-11-06 16 views
20

मेरे पास वस्तुओं की एक सूची है और उन पर एक LINQ क्वेरी है। अब, LINQ के स्थगित निष्पादन के साथ, बाद में फ़ोरैच लूप केवल एक बार या लूप में प्रत्येक मोड़ के लिए क्वेरी निष्पादित करेगा?क्या foreach केवल एक बार क्वेरी निष्पादित करता है?

foreach (int num in numQuery.ToList()) 

इससे कोई फर्क होगा और, अगर:

इस उदाहरण (Introduction to LINQ Queries (C#), on MSDN से लिया) को देखते हुए

// The Three Parts of a LINQ Query: 
    // 1. Data source. 
    int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 }; 

    // 2. Query creation. 
    // numQuery is an IEnumerable<int> 
    var numQuery = 
     from num in numbers 
     where (num % 2) == 0 
     select num; 

    // 3. Query execution. 
    foreach (int num in numQuery) 
    { 
     Console.Write("{0,1} ", num); 
    } 

या, दूसरे शब्दों में, वहाँ कोई अंतर अगर मैं था होगा अंतर्निहित डेटा सरणी में नहीं है, लेकिन डेटाबेस में है?

उत्तर

15

अब, LINQ के स्थगित निष्पादन के साथ, बाद में फ़ोरैच लूप केवल एक बार या लूप में प्रत्येक मोड़ के लिए क्वेरी निष्पादित करेगा?

हाँ, एक बार पाश के लिए। असल में, यह एक से कम क्वेरी को निष्पादित कर सकता है - आप लूपिंग पार्ट के माध्यम से निरस्त कर सकते हैं और (num % 2) == 0 परीक्षण किसी भी शेष आइटम पर नहीं किया जाएगा।

या, दूसरे शब्दों में, वहाँ कोई अंतर अगर मैं था होगा:

foreach (int num in numQuery.ToList()) 

दो मतभेद:

  1. ऊपर मामले में, ToList() कचरे समय और स्मृति , क्योंकि यह पहले प्रारंभिक foreach के रूप में एक ही बात करता है, इसमें से एक सूची है, और फिर foreach कि सूची बनाता है। परिणाम परिणामों के आकार के आधार पर मतभेद कहीं और काम करने से रोकने के लिए कहीं भी होंगे।

  2. हालांकि, इस मामले में जहां आप करने जा रहे हैं बार-बार एक ही परिणाम पर foreach करते हैं, या अन्यथा यह बार-बार उपयोग करते हैं, में तो, जबकि foreach केवल एक बार क्वेरी चलाता है, अगले foreach इसे फिर से चलाता है। यदि क्वेरी महंगी है, तो ToList() दृष्टिकोण (और उस सूची को संग्रहीत करना) एक बड़ी बचत हो सकती है।

+0

हां एक बार। शायद उपरोक्त संपादन के रूप में, क्वेरी के पूरे निष्पादन से भी कम है। –

+0

विस्तार से मुझे समझाए जाने के लिए धन्यवाद! – Marcel

8

नहीं, इससे कोई फर्क नहीं पड़ता। in अभिव्यक्ति का मूल्यांकन एक बार किया जाता है। अधिक विशेष रूप से, foreach निर्माण अभिव्यक्ति पर GetEnumerator() विधि को आमंत्रित करता है और MoveNext() पर बार-बार कॉल करता है और IEnumerable को पार करने के लिए Current संपत्ति तक पहुंचता है।

OTOH, बुला ToList() अनावश्यक है। आपको इसे कॉल करने से परेशान नहीं होना चाहिए।

इनपुट एक डेटाबेस है, तो स्थिति, थोड़ा अलग है के बाद से LINQ IQueryable आउटपुट, लेकिन मैं बहुत यकीन है कि foreach अभी भी एक IEnumerable (जो IQueryable inherits) के रूप में व्यवहार करता है हूँ।

+0

... और यही कारण है एक अपवाद संग्रह बदलने की कोशिश पर फेंक दिया जाता है है किसी के 'foreach'-इंग के माध्यम से। – Alex

+0

@Alex या कड़ाई से, क्यों * इसे * फेंक दिया जा सकता है। 'Foreach' कि संशोधन वादा नहीं करते के लिए नियमों की अनुमति दी जाएगी, लेकिन संग्रह यह अनुमति देने के लिए स्वतंत्र हैं, और कभी कभी (उन जो समवर्ती उपयोग के लिए तैयार कर रहे हैं अगर वे अन्य धागे से एक साथ परिवर्तन को प्रतिबंधित करने के लिए किया था stymied जाएगा) होना आवश्यक है। –

2

लिखा के रूप में, लूप के प्रत्येक यात्रा वास्तव में के रूप में ज्यादा काम करते हैं क्योंकि वह अगले परिणाम लाने के लिए आवश्यक होगा। तो जवाब तकनीकी रूप से "उपरोक्त में से कोई नहीं" होगा। क्वेरी "टुकड़ों में" निष्पादित करेगी।

आप ToList() का उपयोग या किसी अन्य भौतिकीकरण विधि (ToArray() आदि) फिर क्वेरी स्थान और बाद में आपरेशन (जैसे बार-बार दोहराना परिणामों पर के रूप में) पर एक बार मूल्यांकन किया जाएगा बस एक "गूंगा" सूची पर काम करेंगे।

तो numbers एक IEnumerable के बजाय एक IQueryable थे - के रूप में यह संभावना एक डेटाबेस परिदृश्य में हो सकता है - तो ऊपर अभी भी हालांकि नहीं एक पूरी तरह से सटीक वर्णन सच के करीब है। विशेष रूप से, परिणाम को पूरा करने के पहले प्रयास पर क्वेरी करने योग्य प्रदाता डेटाबेस से बात करेगा और परिणाम सेट तैयार करेगा; फिर, इस परिणाम सेट से पंक्तियों को प्रत्येक पुनरावृत्ति पर खींचा जाएगा।

+0

डीबी भाग को समझाने के लिए धन्यवाद। – Marcel

1

जब यह प्रगणित है LINQ क्वेरी निष्पादित किया जाएगा (या तो एक .ToList() कॉल का परिणाम के रूप में या परिणामों पर एक foreach कर।

आप LINQ क्वेरी के परिणामों में दो बार की गणना कर रहे हैं, दोनों बार यह डेटा स्रोत क्वेरी करने के लिए (अपने उदाहरण में, संग्रह की गणना) के रूप में यह है खुद ही एक IEnumerable लौटने का कारण होगा। हालांकि, LINQ क्वेरी के आधार पर, यह हमेशा पूरे संग्रह की गणना नहीं कर सकते हैं (.Any() और .Single() जैसे होगा यदि .Where() है तो पहले ऑब्जेक्ट या पहली मिलान ऑब्जेक्ट पर रोकें)

एक LINQ प्रदाता के कार्यान्वयन विवरण तो हमेशा की तरह व्यवहार अलग हो सकता है जब डेटा स्रोत एक डेटाबेस .ToList() सीधे cache करने के लिए क्वेरी & के परिणाम तुरंत कॉल यह भी सुनिश्चित क्वेरी कि (एफई के मामले में होता है, L2S या NHibernate) एक बार वहाँ मार डाला और फिर है बजाय संग्रह कोड में बाद में कुछ बिंदु पर प्रगणित जाता है और क्वेरी कई बार निष्पादित किया जा रहा है, तो परिणाम कई बार enumerated हैं रोकने के लिए जब।

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