2010-01-07 12 views
17

इनमें से कौन सा आप पसंद करते हैं?सी # foreach बनाम कार्यात्मक प्रत्येक

foreach(var zombie in zombies) 
{ 
    zombie.ShuffleTowardsSurvivors(); 
    zombie.EatNearbyBrains(); 
} 

या

zombies.Each(zombie => { 
    zombie.ShuffleTowardsSurvivors(); 
    zombie.EatNearbyBrains(); 
}); 
+22

आप zombie.Groan करने के लिए कॉल भूल गया(); –

+1

कोई निश्चित रूप से शिकायत करेगा कि यह व्यक्तिपरक है, लेकिन मुझे अभी भी जवाब देखने में दिलचस्पी है। मैं सोच रहा हूं, खुद, अगर इस मामले में 'foreach' के लिए अपनी प्राथमिकता के बावजूद, एक दूसरे को पसंद करने के लिए एक अनिवार्य कारण है। – JMD

+1

मेरा मानना ​​है कि संस्करण 2.0 लाश अब RunLikeHellTowardsSuvivors() का समर्थन करते हैं। –

उत्तर

5

दूसरा रूप है।

मेरी राय में, कम भाषा निर्माणों और जिन कीवर्ड का उपयोग करने के लिए है, बेहतर है। सी # में इसके रूप में पर्याप्त अपर्याप्त क्रूड है।

आम तौर पर कम आप टाइप करने के लिए, बेहतर है। गंभीरता से, आप इस तरह की स्थितियों में "var" का उपयोग कैसे नहीं कर सकते? निश्चित रूप से यदि आपका स्पष्ट लक्ष्य था, तो आप अभी भी हंगेरी नोटेशन का उपयोग कर रहे होंगे ... आपके पास एक आईडीई है जो आपको जब भी होवर करता है तो आपको जानकारी टाइप करता है ... या निश्चित रूप से Ctrl + Q यदि आप Resharper का उपयोग कर रहे हैं .. ।

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

@Reed Copsey: फिर से गैर मानक, अगर एक डेवलपर बाहर काम नहीं कर सकता क्या ".प्रत्येक" तो क्या कर रहा है आप और अधिक समस्याओं मिल गया है, हे। इसे अच्छी तरह से बनाने के लिए भाषा को हैकिंग प्रोग्रामिंग की महान खुशी में से एक है।

+1

@mlangsworth हैं: मैं पता नहीं क्या "प्रत्येक" का अर्थ है । मैं अकेला नहीं हूँ। "ईच" स्पष्ट रूप से एक कस्टम एक्सटेंशन विधि है, या तो IENumerable , IList , या कुछ ठोस संग्रह पर। मुझे कोई जानकारी नहीं है कि यह किसी भी पूर्व या पोस्ट प्रोसेसिंग, या एक्शन को प्राप्त करने के अलावा अन्य कोई अन्य कार्य करता है। मैं अनुमान लगा सकता हूं, लेकिन मुझे नहीं पता, इस कोड से - foreach के साथ, मुझे पता है, ठीक है, यहाँ क्या हो रहा है। –

+0

@ रीड कॉपसी: लेकिन हर विस्तार विधि "कस्टम" है, है ना? शायद अगर विस्तार विधि को "फॉरएच" कहा जाता है? लेकिन चरम पर ले जाया गया, आप कभी नहीं जानते कि कोई भी लाइब्रेरी विधि स्रोत को देखे बिना क्या करती है ... तो क्यों कहा गया है कि उस विधि के नाम और हस्ताक्षर से आपको कुछ और क्यों लगता है? एक विस्तार विधि हस्ताक्षर शून्य foreach (इस IEnumerable सूची, कार्रवाई कार्रवाई) का है, तो मैं आप सुरक्षित रूप से यकीन है कि यह सिर्फ कार्रवाई करने के लिए प्रत्येक तत्व गुजरता मानना। – mlangsworth

+1

क्षमा करें, में अपने कस्टम एक्सटेंशन का स्रोत करने चाहिए थे: .प्रत्येक वास्तव में ईमेल आपकी संपर्क सूची एक यादृच्छिक उद्धरण चक नोरिस में प्रत्येक व्यक्ति को, .Where अपने वॉलपेपर एक जहां के वैली पोस्टर करने के लिए सेट और .Select परिणाम लेता है और अपने चहचहाना के रूप में यह सेट स्थिति – whatupdave

25

पहले। यह किसी कारण के लिए भाषा का हिस्सा है।

व्यक्तिगत रूप से, मैं केवल दूसरा, कार्यात्मक दृष्टिकोण प्रवाह नियंत्रण अगर वहाँ इस तरह के नेट 4 में Parallel.ForEach का उपयोग कर के रूप में ऐसा करने का अच्छा कारण है, का उपयोग करें यह सहित कई नुकसान है:

  • यह धीमा है। यह के लिए प्रत्येक तत्व में एक प्रतिनिधि मंगलाचरण को पेश करने जा रहा है, जैसे तुमने किया था foreach (..) { myDelegate(); }
  • यह गैर मानक है, तो सबसे अधिक डेवलपर्स द्वारा समझने के लिए मुश्किल होगा
  • अगर आप किसी भी स्थानीय लोगों के ऊपर बंद करते हैं, आप के लिए मजबूर करने जा रहे हैं एक बंद करने के लिए संकलक। अगर इसमें थ्रेडिंग शामिल है तो यह अजीब मुद्दों का कारण बन सकता है, और असेंबली में पूरी तरह से अनावश्यक ब्लोट जोड़ता है।

मुझे प्रवाह नियंत्रण निर्माण के लिए अपना स्वयं का वाक्यविन्यास लिखने का कोई कारण नहीं दिखता है जो पहले से ही भाषा में मौजूद है।

+1

यह वास्तव में धीमी नहीं है, मेरी पोस्ट देखें। –

+1

यह सूची .ForEach का उपयोग कर धीमी नहीं है, लेकिन सामान्य रूप में, इस दृष्टिकोण धीमी है। –

-4

मैं या तो पसंद नहीं करता, क्योंकि मैं 'var' का एक अनिवार्य उपयोग मानता हूं। मैं लिखता है के रूप में:

foreach(Zombie zombie in zombies){ 
} 

लेकिन कार्यात्मक या foreach, मेरे लिए मैं सबसे निश्चित रूप से foreach पसंद करते हैं, क्योंकि वहाँ बाद के लिए खास कारण हो प्रतीत नहीं होता लिए के रूप में।

+1

** प्रत्येक ** 'var' का उपयोग अनिवार्य है! यह सिर्फ वाक्य रचनात्मक चीनी है। यह चीजों को आसान बनाता है, यद्यपि। टाइप टाइप क्यों करें, जब संकलक पहले से ही जानता है? – Blorgbeard

+14

वास्तव में? आप ज़ोंबी 3 बार लिखना पसंद करेंगे ?? जैसे हम नहीं जानते कि यह एक ज़ोंबी है ?? – whatupdave

+3

ब्लॉर्गबीर्ड: कुछ बार ऐसा होने पर, मुझे विश्वास है। डेव: हाँ, मैं वास्तव में आवश्यक होने पर 'var' को सहेजना पसंद करूंगा। मैं किसी कारण के लिए स्पष्ट प्रकार वाली भाषा का उपयोग कर रहा हूं :) मुझे यह जानना अच्छा लगता है कि केवल एक नज़र के साथ क्या है। –

1

This question में विषय के दार्शनिक पहलुओं पर कुछ उपयोगी चर्चाएं, साथ ही साथ एमएसडीएन ब्लॉग पोस्ट के लिंक भी शामिल हैं।

0

मुझे लगता है कि दूसरा फॉर्म ऑप्टिमाइज़ करने के लिए कठिन होगा, क्योंकि इस विधि के लिए किसी भी प्रकार के कॉल के लिए किसी भी कॉल के लिए कंपाइलर को लूप को अनलोल करने का कोई तरीका नहीं है।


चूंकि यह पूछा गया था, मैं विस्तार से बता देंगे। विधि का कार्यान्वयन उस कोड से अलग से संकलित करने के लिए काफी उत्तरदायी है जो इसे आमंत्रित करता है। इसका मतलब यह है कि कंपाइलर को यह नहीं पता कि यह कितना लूप करने के लिए किया जा रहा है।

यदि आप "foreach" फॉर्म का उपयोग करते हैं तो उस जानकारी लूप के लिए कोड बनाते समय संकलक के लिए उपलब्ध हो सकता है (यह भी उपलब्ध नहीं हो सकता है, जिसमें कोई फर्क नहीं पड़ता)।

उदाहरण के लिए, यदि संकलक को पता है (उसी फ़ाइल में पिछले कोड से) कि सूची में इसमें 20 आइटम हैं, तो यह पूरे लूप को 20 संदर्भों से प्रतिस्थापित कर सकता है।

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

उल्लेख किया एक और पोस्टर के रूप में, यह है (और होना चाहिए) एक माध्यमिक चिंता का विषय। महत्वपूर्ण बात यह है कि पढ़ने और/या बनाए रखना आसान है, लेकिन मुझे वहां एक बड़ा अंतर दिखाई नहीं देता है।

+0

क्यों नहीं? दोनों परिस्थितियों में दृश्यों के पीछे 'मूवनेक्स्ट' के साथ एक 'आईन्यूमेरेटर' है जिसे मुख्य रूप से देखा जाता है ... मुख्य अंतर यह है कि लैम्ब्डा के साथ, लैम्ब्डा फ़ंक्शन के लिए एक अतिरिक्त कॉल है (जो कहीं भी एक छोटी निजी स्थैतिक विधि है) ; लेकिन JIT कम स्थिर तरीकों इनलाइनिंग पर काफी आक्रामक है, तो यह शायद जब सभी ने कहा और किया जाता है –

+0

TBH मैं अगर यह के साथ एक 'पर foreach' किसी भी छोरों unrolled हैरान होगी सटीक एक ही बात करने के लिए अग्रणी यह ​​इनलाइन जा रहा है, सब। यह वास्तव में केवल भावना जब आप इस तरह के रूप पुराने स्कूल सी शैली पुनरावृत्तियों से जूझ रहे हों बनाता है 'के लिए (int i = 0; i <10; ++ i)' –

4

lamda संस्करण वास्तव में धीमी नहीं है। मैंने अभी एक त्वरित परीक्षण किया है और प्रतिनिधि संस्करण लगभग 30% तेज है।

यहाँ codez है:

class Blah { 
    public void DoStuff() { 
    } 
} 

     List<Blah> blahs = new List<Blah>(); 
     DateTime start = DateTime.Now; 

     for(int i = 0; i < 30000000; i++) { 
      blahs.Add(new Blah()); 
     } 

     TimeSpan elapsed = (DateTime.Now - start); 
     Console.WriteLine(string.Format(System.Globalization.CultureInfo.CurrentCulture, "Allocation - {0:00}:{1:00}:{2:00}.{3:000}", 
     elapsed.Hours, 
     elapsed.Minutes, 
     elapsed.Seconds, 
     elapsed.Milliseconds)); 

     start = DateTime.Now; 

     foreach(var bl in blahs) { 
      bl.DoStuff(); 
     } 

     elapsed = (DateTime.Now - start); 
     Console.WriteLine(string.Format(System.Globalization.CultureInfo.CurrentCulture, "foreach - {0:00}:{1:00}:{2:00}.{3:000}", 
     elapsed.Hours, 
     elapsed.Minutes, 
     elapsed.Seconds, 
     elapsed.Milliseconds)); 

     start = DateTime.Now; 

     blahs.ForEach(bl=>bl.DoStuff()); 

     elapsed = (DateTime.Now - start); 
     Console.WriteLine(string.Format(System.Globalization.CultureInfo.CurrentCulture, "lambda - {0:00}:{1:00}:{2:00}.{3:000}", 
     elapsed.Hours, 
     elapsed.Minutes, 
     elapsed.Seconds, 
     elapsed.Milliseconds)); 

ठीक है, तो मैं और अधिक परीक्षण चलाने के लिए और यहाँ के परिणाम हैं गए हैं।

  1. निष्पादन (forach, लैम्ब्डा या लैम्ब्डा, foreach) ज्यादा फर्क नहीं था के आदेश, लैम्ब्डा संस्करण अभी भी तेजी से किया गया था:

    foreach - 00:00:00.561 
    lambda - 00:00:00.389 
    
    lambda - 00:00:00.317 
    foreach - 00:00:00.337
  2. प्रदर्शन में अंतर बहुत कम है कक्षाओं के सरणी के लिए। यहाँ ब्ला [30000000] के लिए नंबर दिए गए हैं:

    lambda - 00:00:00.317 
    foreach - 00:00:00.337
  3. यहाँ एक ही परीक्षण है, लेकिन ब्ला एक struct किया जा रहा है:

    Blah[] version 
    lambda - 00:00:00.676 
    foreach - 00:00:00.437 
    
    List version: 
    lambda - 00:00:00.461 
    foreach - 00:00:00.391
  4. अनुकूलित का निर्माण, ब्ला एक सरणी का उपयोग कर एक struct है।

    lambda - 00:00:00.426 
    foreach - 00:00:00.079

निष्कर्ष: foreach लैम्ब्डा बनाम के प्रदर्शन के लिए कोई कंबल जवाब नहीं है। जवाब है पर निर्भर करता है। HereList<T> के लिए एक और वैज्ञानिक परीक्षण है। जहां तक ​​मैं कह सकता हूं कि यह बहुत ही कुशल है। क्या तुम सच में प्रदर्शन उपयोग for(int i... पाश के कारण चिंतित हैं। एक हजार ग्राहक रिकॉर्ड (उदाहरण) का एक संग्रह से अधिक पुनरावृत्ति के लिए यह वास्तव में बात यह सब ज्यादा नहीं है।

जहाँ तक कौन-सा संस्करण बीच तय उपयोग करने के लिए मैं नीचे स्थित के लिए लैम्ब्डा संस्करण जिस तरह से संभावित प्रदर्शन हिट कर दिया जाएगा।

निष्कर्ष # 2T[] (जहां टी एक मान प्रकार है) फ़ोरैच लूप एक अनुकूलित अनुकूलित में इस परीक्षण के लिए लगभग 5 गुना तेज है। डीबग और रिलीज बिल्ड के बीच यह एकमात्र महत्वपूर्ण अंतर है। तो वहां आप जाते हैं, मूल्य प्रकारों के सरणी फोरैच का उपयोग करते हैं, बाकी सब कुछ - इससे कोई फर्क नहीं पड़ता।

+0

यह दिलचस्प और अप्रत्याशित है। सूची .Enumerator अधिक भूमि के ऊपर की तुलना में मैं अनुमान लगाया है होता परिचय है, लेकिन ध्यान दें कि यह यात्रा, जबकि सूची .ForEach नहीं (कम से कम, अपने स्थानीय .NET विधानसभाओं में) करता है के दौरान संग्रह संशोधन का पता लगाता है। – Trillian

+0

यहां सबसे बड़ी बात यह है कि आप इसे दूसरे कॉल करके लैम्ब्डा संस्करण का लाभ दे रहे हैं। फोरैच लूप में जेआईटी ओवरहेड लगेगा जिसमें मापा गया समय शामिल किया जा रहा है। दूसरा (जैसा कि ट्रिलियन का उल्लेख है) सूची गणनाकर्ता विशेष रूप से नंगे धातु नहीं है। टी [] पर फोरैच का उपयोग करने से बेहतर प्रदर्शन अभी भी बेहतर होगा जो लैम्बडा दृष्टिकोण> मेरी मशीन पर 2x तेज से बेहतर है। कच्चा प्रदर्शन महत्वपूर्ण हो सकता है या नहीं भी हो सकता है, लेकिन लैम्ब्डा दृष्टिकोण के अतिरिक्त संकेत के लिए मापनीय लागत है। यदि ब्लाह एक मूल्य प्रकार है, तो यह बहुत नाटकीय है। – homeInAStar

+2

यह सूची के साथ सच है। प्रत्येक के लिए, लेकिन सामान्य रूप से नहीं। उन्होंने स्पष्ट रूप से इसे "प्रत्येक" के रूप में सूचीबद्ध किया था, सूची की फ़ॉरएच विधि नहीं है। –

10

यहां आप अभिव्यक्ति के बजाए एक कथन लिखने जैसी कुछ अनिवार्य चीजें कर रहे हैं (संभवतः Each विधि कोई मूल्य नहीं देता है) और उत्परिवर्तित स्थिति (जो केवल विधियों को ही मान सकता है, क्योंकि वे वापस लौटने लगते हैं कोई मूल्य नहीं) फिर भी आप एक प्रतिनिधि के रूप में बयानों के संग्रह को पारित करके 'कार्यात्मक प्रोग्रामिंग' के रूप में उन्हें पास करने की कोशिश कर रहे हैं। यह कोड आदर्श रूप से कार्यात्मक प्रोग्रामिंग के आदर्शों और मुहावरों से आगे हो सकता है, तो इसे इस तरह छिपाने का प्रयास क्यों करें?

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

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

1

मुझे लगता है कि विस्तार के तरीकों शांत हैं, लेकिन मुझे लगता है कि break और संपादित और जारी रखने के लिए कूलर कर रहे हैं।

+0

विस्तार विधियां किसी भी तरह से संपादन-और-जारी को प्रभावित करती हैं? मुझे यह नहीं मिला: -एस –

+0

एक्सटेंशन विधियां नहीं हैं, लेकिन अनाम विधियां करते हैं। –

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