जहां एक ओर हालत बनाम गार्ड क्लॉस जारी रहता है, वहां से फॉरेस्ट लूप को छानना


24

मैंने देखा है कुछ प्रोग्रामर इसका उपयोग करते हैं:

foreach (var item in items)
{
    if (item.Field != null)
        continue;

    if (item.State != ItemStates.Deleted)
        continue;

    // code
}

इसके बजाय जहाँ मैं सामान्य रूप से उपयोग करूँगा:

foreach (var item in items.Where(i => i.Field != null && i.State != ItemStates.Deleted))
{
    // code
}

मैंने दोनों का एक संयोजन भी देखा है। मुझे वास्तव में 'जारी' के साथ पठनीयता पसंद है, खासकर अधिक जटिल परिस्थितियों के साथ। क्या प्रदर्शन में भी अंतर है? एक डेटाबेस क्वेरी के साथ मैं मान रहा हूँ कि वहाँ होगा। नियमित सूचियों के बारे में क्या?


3
नियमित सूचियों के लिए यह सूक्ष्म अनुकूलन जैसा लगता है।
सर्वनाश

2
@zgnilec: ... लेकिन वास्तव में दो वेरिएंट में से कौन सा एक अनुकूलित संस्करण है? मेरे पास इस बारे में एक राय है, ज़ाहिर है, लेकिन कोड को देखने से यह सभी के लिए स्वाभाविक रूप से स्पष्ट नहीं है।
डॉक ब्राउन

2
संभोग जारी रहेगा तेजी से। Linq का उपयोग करना। कहीं आप अतिरिक्त iterator बना रहे हैं।
सर्वनाश

1
@ ज़्गैनइलेक - अच्छा सिद्धांत। उत्तर देने के लिए ध्यान दें कि आप ऐसा क्यों सोचते हैं? दोनों उत्तर जो वर्तमान में मौजूद हैं, विपरीत कहते हैं।
बोबसन

2
... तो नीचे की रेखा यह है: दो निर्माणों के बीच प्रदर्शन के अंतर उपेक्षित हैं, और पठनीयता के साथ-साथ दोनों के लिए डिबगैबिलिटी हासिल की जा सकती है। यह केवल स्वाद की बात है जिसे आप पसंद करते हैं।
Doc Brown

जवाबों:


64

मैं इसे कमांड / क्वेरी पृथक्करण का उपयोग करने के लिए एक उपयुक्त स्थान के रूप में मानूंगा । उदाहरण के लिए:

// query
var validItems = items.Where(i => i.Field != null && i.State != ItemStates.Deleted);
// command
foreach (var item in validItems) {
    // do stuff
}

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

डिबगिंग करते समय, आप foreachजल्दी से जांच कर सकते हैं कि validItemsक्या आप उम्मीद करते हैं कि समाधान की सामग्री । जब तक आपको जरूरत न हो, आपको लैम्ब्डा में कदम रखने की जरूरत नहीं है। यदि आपको लैम्ब्डा में कदम रखने की आवश्यकता है, तो मैं इसे एक अलग फ़ंक्शन में फैक्टरिंग करने का सुझाव देता हूं, फिर इसके बजाय इसके माध्यम से कदम बढ़ाएं।

क्या प्रदर्शन में अंतर है? यदि क्वेरी डेटाबेस द्वारा समर्थित है, तो LINQ संस्करण में तेज़ी से चलने की क्षमता है, क्योंकि SQL क्वेरी अधिक कुशल हो सकती है। यदि यह LINQ ऑब्जेक्ट्स के लिए है, तो आपको कोई वास्तविक प्रदर्शन अंतर दिखाई नहीं देगा। हमेशा की तरह, अपने कोड को प्रोफाइल करें और उन अड़चनों को ठीक करें जो वास्तव में रिपोर्ट की गई हैं, बजाय अग्रिम में अनुकूलन के पूर्वानुमान की कोशिश करने के।


1
एक बहुत बड़े डेटा सेट से फर्क क्यों पड़ेगा? सिर्फ इसलिए कि मेमनों की कम लागत में अंततः वृद्धि होती?
ब्लूराजा - डैनी पफ्लुगुएफ्ट '

1
@ BlueRaja-DannyPflughoeft: हां आप सही कह रहे हैं, इस उदाहरण में मूल कोड से परे कोई अतिरिक्त एल्गोरिथम जटिलता नहीं है। मैंने वाक्यांश निकाल दिया है।
क्रिश्चियन हैटर

क्या संग्रह के दो पुनरावृत्तियों में इसका परिणाम नहीं है? स्वाभाविक रूप से, दूसरा एक छोटा है, केवल वैध वस्तुओं को देखते हुए, इसके भीतर है, लेकिन आपको अभी भी इसे दो बार करने की आवश्यकता है, एक बार तत्वों को छानने के लिए, दूसरी बार वैध वस्तुओं के साथ काम करने के लिए।
एंडी

1
@DavidPacker नंबर केवल लूप IEnumerableद्वारा संचालित किया जा रहा है foreach
बेंजामिन हॉजसन

2
@ डैविडपैकर: बिल्कुल यही करता है; अधिकांश LINQ से ऑब्जेक्ट्स विधि इट्रेटर ब्लॉक का उपयोग करके कार्यान्वित की जाती हैं। ऊपर दिया गया उदाहरण कोड, संग्रह के माध्यम से ठीक एक बार, Whereलैम्बडा और लूप बॉडी को निष्पादित करेगा (यदि लैम्बडा सही है) प्रति तत्व एक बार।
क्रिश्चियन हैदर

7

बेशक प्रदर्शन में अंतर है, .Where()हर एक आइटम के लिए एक प्रतिनिधि कॉल किया जा रहा है। हालांकि, मैं प्रदर्शन के बारे में बिल्कुल चिंता नहीं करूंगा:

  • एक प्रतिनिधि को आमंत्रित करने में उपयोग किए जाने वाले घड़ी चक्र, बाकी कोड द्वारा उपयोग किए गए घड़ी चक्रों की तुलना में नगण्य हैं जो संग्रह पर पुनरावृत्त करते हैं और शर्तों की जांच करते हैं।

  • एक प्रतिनिधि को आमंत्रित करने का प्रदर्शन दंड कुछ घड़ी चक्रों के क्रम का है, और सौभाग्य से, हम उन दिनों से बहुत पहले हैं जब हमें व्यक्तिगत घड़ी चक्रों के बारे में चिंता करनी थी।

यदि किसी कारण से प्रदर्शन आपके लिए घड़ी चक्र स्तर पर वास्तव में महत्वपूर्ण है, तो List<Item>इसके बजाय का उपयोग करें IList<Item>, ताकि कंपाइलर वर्चुअल कॉल के बजाय डायरेक्ट (और इनलाइन) कॉल का उपयोग कर सके, और ताकि इट्रेटर List<T>, जो वास्तव में है a struct, को बॉक्सिंग नहीं करना है। लेकिन यह वास्तव में सामान trifling है।

डेटाबेस क्वेरी एक अलग स्थिति है, क्योंकि आरडीबीएमएस को फ़िल्टर भेजने की संभावना (कम से कम सिद्धांत में) है, इस प्रकार प्रदर्शन में बहुत सुधार होता है: केवल मेल खाने वाली पंक्तियाँ आपके प्रोग्राम के लिए आरडीबीएमएस से यात्रा करेंगी। लेकिन इसके लिए मुझे लगता है कि आपको linq का उपयोग करना होगा, मुझे नहीं लगता कि यह अभिव्यक्ति RDBMS को भेजी जा सकती है क्योंकि यह है।

आप वास्तव में if(x) continue;इस कोड को डीबग करने के क्षण के लाभों को देखेंगे : सिंगल-स्टेप ओवर if()एस एंड continueएस अच्छी तरह से काम करता है; फ़िल्टरिंग प्रतिनिधि में एकल-कदम एक दर्द है।


यह तब होता है जब कुछ गलत होता है और आप सभी आइटमों को देखना चाहते हैं और डीबगर में जाँचते हैं कि किन लोगों के पास फ़ील्ड है? = Null, और किन लोगों के पास State! = Null है! यह मुश्किल के लिए मुश्किल के साथ हो सकता है ... जहां।
gnasher729

डिबगिंग के साथ अच्छा बिंदु। जहां दृश्य स्टूडियो में बुरा नहीं है, वहां कदम रखना, लेकिन आप पुनर्मिलन के बिना डिबगिंग करते समय लैम्ब्डा अभिव्यक्तियों को फिर से नहीं लिख सकते हैं और इसका उपयोग करने से बचें if(x) continue;
पप्रीक

कड़ाई से बोलने पर, .Whereकेवल एक बार आह्वान किया जाता है। क्या प्रत्येक यात्रा पर लागू हो जाता है फिल्टर प्रतिनिधि है (और MoveNextऔर Currentप्रगणक, जब वे बाहर अनुकूलित नहीं किए जाते हैं पर)
CodesInChaos

@CodesInChaos ने मुझे यह समझने में थोड़ा सा समझ लिया कि आप किस बारे में बात कर रहे हैं, लेकिन निश्चित रूप से, wh00ps, आप सही हैं, कड़ाई से बोल रहे हैं, .Whereकेवल एक बार आह्वान किया जाता है। ठीक कर दिया।
माइक नाकिस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.