c# - foreach बनाम कुछ सूची। ForEach(){}




.net generics (9)

संग्रह पर फिर से शुरू करने के कई तरीके हैं। यदि कोई अंतर है, या आप दूसरे पर एक तरफ क्यों उपयोग करेंगे तो उत्सुक है।

पहला प्रकार:

List<string> someList = <some way to init>
foreach(string s in someList) {
   <process the string>
}

अन्य रास्ता:

List<string> someList = <some way to init>
someList.ForEach(delegate(string s) {
    <process the string>
});

मुझे लगता है कि मेरे सिर के ऊपर से, कि अज्ञात प्रतिनिधि के बजाय मैं ऊपर उपयोग करता हूं, आपके पास एक पुन: प्रयोज्य प्रतिनिधि होगा जो आप निर्दिष्ट कर सकते हैं ...


List.ForEach () को अधिक कार्यात्मक माना जाता है।

List.ForEach() कहता है कि आप क्या करना चाहते हैं। foreach(item in list) यह भी कहता है कि आप इसे कैसे करना चाहते हैं। यह सूची छोड़ देता है। भविष्य में कैसे भाग के कार्यान्वयन को बदलने के लिए स्वतंत्र है। उदाहरण के लिए, नेट का एक काल्पनिक भविष्य संस्करण हमेशा सूची चला सकता है। समानांतर में, इस धारणा के तहत कि इस बिंदु पर हर किसी के पास कई सीपीयू कोर होते हैं जो आम तौर पर निष्क्रिय रहते हैं।

दूसरी ओर, foreach (item in list) आपको लूप पर थोड़ा अधिक नियंत्रण देता है। उदाहरण के लिए, आप जानते हैं कि आइटम किसी प्रकार के क्रमिक क्रम में पुनरावृत्त किए जाएंगे, और यदि कोई आइटम कुछ शर्त प्राप्त करता है तो आप आसानी से बीच में तोड़ सकते हैं।


List.foreach प्रदर्शन पर निम्न article पर विचार करें।


आपके द्वारा दिखाया गया दूसरा तरीका सूची में प्रत्येक तत्व के लिए प्रतिनिधि विधि निष्पादित करने के लिए एक विस्तार विधि का उपयोग करता है।

इस तरह, आपके पास एक और प्रतिनिधि (= विधि) कॉल है।

इसके अतिरिक्त, लूप के साथ सूची को फिर से शुरू करने की संभावना है।


दृश्यों के पीछे, अज्ञात प्रतिनिधि वास्तविक विधि में बदल जाता है ताकि आप दूसरे विकल्प के साथ कुछ ओवरहेड प्राप्त कर सकें यदि संकलक फ़ंक्शन को इनलाइन करने का विकल्प नहीं चुनता है। इसके अतिरिक्त, गुमनाम प्रतिनिधि उदाहरण के शरीर द्वारा संदर्भित किसी भी स्थानीय चर प्रकृति में बदल जाएंगे क्योंकि संकलक चालें इस तथ्य को छिपाने के लिए करती हैं कि यह एक नई विधि में संकलित हो जाती है। सी # यह जादू कैसे करता है इस बारे में अधिक जानकारी यहां:

http://blogs.msdn.com/oldnewthing/archive/2006/08/04/688527.aspx


फॉरएच फ़ंक्शन जेनेरिक क्लास सूची का सदस्य है।

मैंने आंतरिक कोड को पुन: पेश करने के लिए निम्न एक्सटेंशन बनाया है:

public static class MyExtension<T>
    {
        public static void MyForEach(this IEnumerable<T> collection, Action<T> action)
        {
            foreach (T item in collection)
                action.Invoke(item);
        }
    }

तो अंत हम एक सामान्य foreach (या यदि आप चाहते हैं के लिए एक लूप) का उपयोग कर रहे हैं।

दूसरी ओर, एक प्रतिनिधि समारोह का उपयोग करना एक समारोह को परिभाषित करने का एक और तरीका है, यह कोड:

delegate(string s) {
    <process the string>
}

के बराबर है:

private static void myFunction(string s, <other variables...>)
{
   <process the string>
}

या labda अभिव्यक्तियों का उपयोग कर:

(s) => <process the string>

मस्ती के लिए, मैंने परावर्तक में सूची पॉप अप की और यह परिणामस्वरूप सी #:

public void ForEach(Action<T> action)
{
    if (action == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
    }
    for (int i = 0; i < this._size; i++)
    {
        action(this._items[i]);
    }
}

इसी प्रकार, एन्यूमेरेटर में MoveNext जो कि foreach द्वारा उपयोग किया जाता है यह है:

public bool MoveNext()
{
    if (this.version != this.list._version)
    {
        ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
    }
    if (this.index < this.list._size)
    {
        this.current = this.list._items[this.index];
        this.index++;
        return true;
    }
    this.index = this.list._size + 1;
    this.current = default(T);
    return false;
}

List.ForEach MoveNext की तुलना में बहुत अधिक छंटनी की गई है - बहुत कम प्रोसेसिंग - जेआईटी को कुछ कुशलता से अधिक संभावना होगी ..

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


मैं दो अस्पष्ट-आशों की चीजें जानता हूं जो उन्हें अलग बनाती हैं। मुझे जाओ!

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

    // A list of actions to execute later
    List<Action> actions = new List<Action>();

    // Numbers 0 to 9
    List<int> numbers = Enumerable.Range(0, 10).ToList();

    // Store an action that prints each number (WRONG!)
    foreach (int number in numbers)
        actions.Add(() => Console.WriteLine(number));

    // Run the actions, we actually print 10 copies of "9"
    foreach (Action action in actions)
        action();

    // So try again
    actions.Clear();

    // Store an action that prints each number (RIGHT!)
    numbers.ForEach(number =>
        actions.Add(() => Console.WriteLine(number)));

    // Run the actions
    foreach (Action action in actions)
        action();

List.ForEach विधि में यह समस्या नहीं है। पुनरावृत्ति के वर्तमान आइटम को बाहरी लैम्ब्डा के लिए तर्क के रूप में मूल्य से पारित किया जाता है, और फिर आंतरिक लैम्ब्डा अपने तर्क को सही ढंग से उस तर्क को कैप्चर करता है। समस्या सुलझ गयी।

(दुख की बात है कि मेरा मानना ​​है कि फॉरएच एक विस्तार विधि के बजाय सूची का सदस्य है, हालांकि इसे स्वयं परिभाषित करना आसान है, इसलिए आपके पास यह सुविधा किसी भी प्रकार के प्रकार पर है।)

दूसरा, फॉरएच विधि दृष्टिकोण में एक सीमा है। यदि आप उपज रिटर्न का उपयोग कर आईन्यूमेरेबल को कार्यान्वित कर रहे हैं, तो आप लैम्ब्डा के अंदर उपज रिटर्न नहीं कर सकते हैं। इसलिए इस विधि से वापसी की चीजों को उत्पन्न करने के लिए संग्रह में वस्तुओं के माध्यम से लूपिंग संभव नहीं है। आपको लूप के अंदर वर्तमान लूप वैल्यू की प्रतिलिपि बनाकर फोरैच कीवर्ड का उपयोग करना होगा और क्लोजर समस्या के आसपास काम करना होगा।

यहां अधिक


संपूर्ण फॉरएच स्कोप (प्रतिनिधि फ़ंक्शन) को कोड की एक पंक्ति (फ़ंक्शन को कॉल करने) के रूप में माना जाता है, और आप कोड में ब्रेकपॉइंट्स या चरण सेट नहीं कर सकते हैं। यदि एक अनचाहे अपवाद होता है तो पूरे ब्लॉक को चिह्नित किया जाता है।


हमारे यहां कुछ कोड था ( list.ForEach( delegate(item) { foo;}); और सी # 2.0 में) जहां पिछले इंजीनियर सूची का उपयोग करने के लिए अपने रास्ते से बाहर चले गए थे। list.ForEach( delegate(item) { foo;}); foreach(item in list) {foo; }; बजाय foreach(item in list) {foo; }; foreach(item in list) {foo; }; उनके द्वारा लिखे गए सभी कोड के लिए। उदाहरण के लिए डेटा रीडर से पंक्तियों को पढ़ने के लिए कोड का एक ब्लॉक।

मुझे अभी भी पता नहीं है कि उन्होंने ऐसा क्यों किया।

सूची की कमी। list.ForEach() हैं:

  • यह सी # 2.0 में अधिक verbose है। हालांकि, सी # 3 में, आप कुछ अच्छी तरह से terse अभिव्यक्तियों को बनाने के लिए " => " वाक्यविन्यास का उपयोग कर सकते हैं।

  • यह कम परिचित है। जो लोग इस कोड को बनाए रखना चाहते हैं, वे आश्चर्यचकित होंगे कि आपने ऐसा क्यों किया। यह निर्णय लेने में मुझे थोड़ी देर लग गई कि लेखक को चालाक लगने के अलावा, किसी भी कारण का कोई कारण नहीं था (शेष कोड की गुणवत्ता उसमें कमजोर है)। यह प्रतिनिधि कोड ब्लॉक के अंत में " }) " के साथ भी कम पठनीय था।

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

  • एक foreach(item in list) निर्माण आपको break का उपयोग करने या continue अनुमति देता है अगर आपको पुनरावृत्ति या लूप से बाहर निकलने की आवश्यकता है। लेकिन आप फोरैच लूप के अंदर सूची को बदल नहीं सकते हैं।

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

मैं उस सूची से असहमत हूं। किसी भी महत्वपूर्ण तरीके से list.ForEach(delegate) संस्करण "अधिक कार्यात्मक" है। यह एक समारोह में एक समारोह पास करता है, लेकिन परिणाम या कार्यक्रम संगठन में कोई बड़ा अंतर नहीं है।

मुझे नहीं लगता कि foreach(item in list) "बिल्कुल कहता है कि आप इसे कैसे करना चाहते हैं" - एक for(int 1 = 0; i < count; i++) लूप करता है, एक foreach लूप नियंत्रण की पसंद छोड़ देता है कंपाइलर

मेरी सोच एक नई परियोजना पर है, सामान्य उपयोग और पठनीयता के लिए सबसे लूप के लिए foreach(item in list) का उपयोग करने के लिए, और सूची का उपयोग करें। list.Foreach() केवल छोटे ब्लॉक के लिए, जब आप कुछ और कर सकते हैं सी # 3 " => " ऑपरेटर के साथ सुंदर या कॉम्पैक्टली। ऐसे मामलों में, पहले से ही एक LINQ एक्सटेंशन विधि हो सकती है जो ForEach() से अधिक विशिष्ट है। देखें कि Where() , Select() , Any() , All() , Max() या कई अन्य LINQ विधियों में से एक लूप से जो भी आप चाहते हैं वह पहले से नहीं करता है।







enumeration