2010-03-10 5 views
38

इस obfuscated कोड के इस बिट पर विचार करें। इसका उद्देश्य अज्ञात कन्स्ट्रक्टर और yield return के माध्यम से फ्लाई पर एक नई वस्तु बनाना है। लक्ष्य केवल return पर स्थानीय संग्रह को बनाए रखने से बचने के लिए है।सी #: एक foreach के भीतर उपज वापसी विफल रहता है - शरीर एक iterator ब्लॉक नहीं हो सकता

public static List<DesktopComputer> BuildComputerAssets() 
{   
    List<string> idTags = GetComputerIdTags(); 

    foreach (var pcTag in idTags) 
    { 
     yield return new DesktopComputer() {AssetTag= pcTag 
              , Description = "PC " + pcTag 
              , AcquireDate = DateTime.Now 
              }; 
    }    
} 

दुर्भाग्य से, कोड के इस बिट एक अपवाद पैदा करता है:

त्रुटि 28 के शरीर 'Foo.BuildComputerAssets()' पुनरावर्तक ब्लॉक नहीं किया जा सकता क्योंकि 'System.Collections.Generic.List' पुनरावर्तक इंटरफ़ेस प्रकार नहीं है

प्रश्न

  • इस त्रुटि संदेश का क्या अर्थ है?
  • मैं इस त्रुटि से कैसे बच सकता हूं और yield return का उपयोग कैसे कर सकता हूं?

उत्तर

49

आप केवल एक समारोह है कि एक IEnumerable या एक IEnumerator, नहीं एक List<T> रिटर्न में yield return उपयोग कर सकते हैं।

आपको IEnumerable<DesktopComputer> वापस करने के लिए अपने फ़ंक्शन को बदलने की आवश्यकता है।

वैकल्पिक रूप से, आप को फिर से लिखने कर सकते हैं समारोह List<T>.ConvertAll उपयोग करने के लिए:

return GetComputerIdTags().ConvertAll(pcTag => 
    new DesktopComputer() { 
     AssetTag = pcTag, 
     Description = "PC " + pcTag, 
     AcquireDate = DateTime.Now 
    }); 
16

आपका विधि हस्ताक्षर गलत है। यह होना चाहिए:

public static IEnumerable<DesktopComputer> BuildComputerAssets() 
8

yield केवल इटरेटर प्रकार पर काम करता है:

उपज बयान केवल एक iterator ब्लॉक

Iterators

के रूप में परिभाषित कर रहे हैं अंदर दिखाई दे सकता है एक पुनरावर्तक का रिटर्न प्रकार IENumerable, IENumerator, IENume होना चाहिए rable < टी >, या आईन्यूमेरेटर < टी >।

IList और IList < टी > IEnumerable/IEnumerable < टी > लागू करते हैं, लेकिन एक प्रगणक के लिए हर फोन करने वाले उपरोक्त चार प्रकारों में से एक और दूसरा कोई उम्मीद है।

2

आप LINQ क्वेरी (सी # 3.0+ में) का उपयोग करके समान कार्यक्षमता को भी कार्यान्वित कर सकते हैं। यह ConvertAll विधि का उपयोग करने से कम कुशल है, लेकिन यह अधिक सामान्य है। बाद में, आप भी इस तरह के छानने के रूप में अन्य LINQ सुविधाओं का उपयोग करने की आवश्यकता हो सकती:

return (from pcTag in GetComputerIdTags() 
     select new DesktopComputer() { 
      AssetTag = pcTag, 
      Description = "PC " + pcTag, 
      AcquireDate = DateTime.Now 
     }).ToList(); 

ToList विधि IEnumerable<T> से List<T> के परिणाम बदल देता है।मुझे व्यक्तिगत रूप से ConvertAll पसंद नहीं है, क्योंकि यह LINQ जैसा ही काम करता है। लेकिन क्योंकि इसे पहले जोड़ा गया था, इसका उपयोग LINQ के साथ नहीं किया जा सकता है (इसे Select कहा जाना चाहिए था)।

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