2017-08-25 7 views
7

इस कोड को निम्नलिखित उत्पादन मेंकहां बनाम foreach के साथ - क्यों अलग परिणाम?

using System; 
using System.Collections.Generic; 
using System.Linq; 

namespace ConsoleApplication 
{ 
    internal class Program 
    { 
     public static void Main() 
     { 
      var values = new[] {1, 2, 3, 3, 2, 1, 4}; 
      var distinctValues = GetDistinctValuesUsingWhere(values); 
      Console.WriteLine("GetDistinctValuesUsingWhere No1: " + string.Join(",", distinctValues)); 
      Console.WriteLine("GetDistinctValuesUsingWhere No2: " + string.Join(",", distinctValues)); 
      distinctValues = GetDistinctValuesUsingForEach(values); 
      Console.WriteLine("GetDistinctValuesUsingForEach No1: " + string.Join(",", distinctValues)); 
      Console.WriteLine("GetDistinctValuesUsingForEach No2: " + string.Join(",", distinctValues)); 
      Console.ReadLine(); 
     } 

     private static IEnumerable<T> GetDistinctValuesUsingWhere<T>(IEnumerable<T> items) 
     { 
      var set=new HashSet<T>(); 
      return items.Where(i=> set.Add(i)); 
     } 

     private static IEnumerable<T> GetDistinctValuesUsingForEach<T>(IEnumerable<T> items) 
     { 
      var set=new HashSet<T>(); 
      foreach (var i in items) 
      { 
       if (set.Add(i)) 
        yield return i; 
      } 
     } 
    } 
} 

परिणाम:

GetDistinctValuesUsingWhere नं .1: 1,2,3,4

GetDistinctValuesUsingWhere No2:

GetDistinctValuesUsingForEach नं .1: 1,2 , 3,4

GetDistinctValuesUsingForEac एच संख्या 2: 1,2,3,4

मुझे समझ में नहीं आता कि मुझे "GetDistinctValuesUsingWhere No2" पंक्ति में कोई मूल्य क्यों नहीं मिलता है।

क्या कोई मुझे यह समझा सकता है?

अद्यतन स्कॉट से जवाब के बाद, मैं निम्नलिखित करने के लिए उदाहरण बदल दिया है:

 private static IEnumerable<T> GetDistinctValuesUsingWhere2<T>(IEnumerable<T> items) 
    { 
     var set = new HashSet<T>(); 
     var capturedVariables = new CapturedVariables<T> {set = set}; 

     foreach (var i in items) 
      if (capturedVariables.set.Add(i)) 
       yield return i; 
     //return Where2(items, capturedVariables); 
    } 

    private static IEnumerable<T> Where2<T>(IEnumerable<T> source, CapturedVariables<T> variables) 
    { 
     foreach (var i in source) 
      if (variables.set.Add(i)) 
       yield return i; 
    } 

    private class CapturedVariables<T> 
    { 
     public HashSet<T> set; 
    } 

यह दो बार उत्पादन 1,2,3,4 का परिणाम देगा।

हालांकि, अगर मैं सिर्फ लाइन uncomment

वापसी Where2 (आइटम, capturedVariables);

और टिप्पणी लाइनों

foreach (मदों में वर i) अगर (capturedVariables.set.Add (i)) उपज वापसी मैं;

विधि में GetDistinctValuesUsingWhere2, मुझे केवल एक बार आउटपुट 1,2,3,4 मिलेगा। यह हटाए गए लाइनों की पूरी तरह से है और अब-असम्बद्ध विधि बिल्कुल वही हैं।

मैं अभी भी यह नहीं मिलता है ....

+0

ऑफ-विषय, लेकिन क्यों न केवल 'अलग' (या गुणों के साथ वस्तुओं, MorelinQ की 'DistinctBy') का उपयोग क्यों करें? या यह सिर्फ एक सीखने का अभ्यास है? –

+0

अपने कहां के अंत में '.TList() 'जोड़ने का प्रयास करें, आपको सही परिणाम मिलना चाहिए। समस्या यह है कि आप अभिव्यक्ति वापस कर रहे हैं, इसलिए जब भी आप ऑब्जेक्ट का अनुरोध करेंगे तो इसका पुनर्मूल्यांकन किया जाएगा। – Kolichikov

+0

संभावित डुप्लिकेट: [कैसे बताएं कि एक आईनेमरेबल स्थगित निष्पादन के अधीन है?] (Https://stackoverflow.com/questions/1168944/how-to-tell-if-an-ienumerablet-is-subject-to- स्थगित-निष्पादन) – Igor

उत्तर

3

अद्यतन संस्करण का जवाब:

  • foreach पाश युक्त GetDistinctValuesUsingWhere2() विधि के मामले में, लौटे IEnumerableset प्रारंभ बयान सहित एक बंद में विधि की पूरी सामग्री पर कब्जा कर लिया।इस कथन को हर बार जब आप पर मूल कॉल के दौरान संख्यात्मक, को पुन: प्रारंभ करना शुरू करते हैं, लेकिन नहीं करते हैं।
  • अन्य संस्करण के मामले में, जहां आप Where2() लौटाते हैं, GetDistinctValuesUsingWhere2() विधि को विधि की सामग्री को कैप्चर करने की आवश्यकता नहीं है क्योंकि आपने इसमें एक इटरेटर या प्रतिनिधि को परिभाषित नहीं किया है। इसके बजाय, आप IEnumerable के रूप में Where2() लौटते हैं। बाद की विधि केवल foreach लूप और उसके पैरामीटर (पहले से प्रारंभ की गई) को कैप्चर करती है, लेकिन set प्रारंभिक विवरण स्वयं ही नहीं। इस प्रकार, सेट प्रारंभिक विवरण केवल मूल कॉल के दौरान GetDistinctValuesUsingWhere2() पर निष्पादित किया जाएगा।

यदि आवश्यक हो, तो अपने कोड में विभिन्न बिंदुओं पर कुछ ब्रेकपॉइंट डालें: इससे आपको समझने में मदद करनी चाहिए कि मैंने यहां क्या समझाया है।

+0

मुझे लगता है कि यह उत्तर दो अलग-अलग तरीकों को समझाने का बेहतर काम करता है। –

8

कारण GetDistinctValuesUsingWhere No2 कोई फल प्राप्त नहीं करता चर पर कब्जा के कारण है।

आपका जहां विधि अधिक इस समारोह

private static IEnumerable<T> GetDistinctValuesUsingWhere<T>(IEnumerable<T> items) 
    { 
     var set=new HashSet<T>(); 
     var capturedVariables = new CapturedVariables {set = set} 
     return Where(items, capturedVariables); 
    } 

    IEnumerable<T> Where(IEnumerable<T> source, CapturedVariables variables) 
    { 
     foreach (var i in items) 
     { 
      if (variables.set.Add(i)) 
       yield return i; 
     } 

    } 

तो दोनों तरीकों हुड के नीचे yield return हैं की तरह है, लेकिन GetDistinctValuesUsingWhere प्रत्येक मंगलाचरण जहां GetDistinctValuesUsingForEach एक नया HashSet प्रत्येक गणन उत्पन्न करता है के लिए HashSet पुनः उपयोग कर लेता।

+0

समस्या यह है कि, मुझे नहीं लगता कि सीएलआर वास्तव में क्यों या कहां तय करता है कि इसे कैप्चर किए गए चर को जीवित रखने की आवश्यकता है। यदि आप अपनी "कहां" विधि में कोड को रेखांकित करते हैं, तो चर को कैप्चर नहीं किया जाएगा, और उदाहरण एकाधिक कॉल पर अपेक्षित काम करता है। (मेरा अद्यतन उदाहरण देखें)। –

+1

@umei 'उपज रिटर्न' कथन वाला एक तरीका कम से कम गणना के अंत तक "जीवित रहेंगे" (पूरी विधि जिंदा रहेगी)। या दूसरे शब्दों में, गणना करने योग्य संपूर्ण विधि का प्रतिनिधित्व करता है (पूरी विधि को राज्य मशीन में परिवर्तित कर दिया जाता है)। 'Foreach' संस्करण के लिए, लैम्ब्डा अभिव्यक्ति 'हैशसेट' के संदर्भ को कैप्चर करती है, और गणना करने योग्य केवल 'हैश' के साथ उस लैम्ब्डा के अनुप्रयोग का प्रतिनिधित्व करता है, न कि 'हैशसेट' की तात्कालिकता। नया 'हैशसेट' प्राप्त करने के लिए आपको दूसरी बार 'GetDistinctValuesUsingWhere'' का आह्वान करना होगा। –

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