2010-02-08 11 views
17

क्या LINQ कथन के रूप में निम्नलिखित 'foreach' लिखना संभव है, और मुझे लगता है कि लूप के लिए किसी भी सामान्य प्रश्न को LINQ कथन द्वारा प्रतिस्थापित किया जा सकता है।क्या सभी 'for' loops को LINQ कथन के साथ प्रतिस्थापित किया जा सकता है?

मुझे पारंपरिक रूप से अनिवार्य कोड में घोषणात्मक दृष्टिकोणों का उपयोग करने की संभावित क्षमता लागत में कोई दिलचस्पी नहीं है।

private static string SomeMethod() 
    { 
     if (ListOfResources .Count == 0) 
      return string.Empty; 

     var sb = new StringBuilder(); 
     foreach (var resource in ListOfResources) 
     { 
      if (sb.Length != 0) 
       sb.Append(", "); 

      sb.Append(resource.Id); 
     } 

     return sb.ToString(); 
    } 

चीयर्स

आंगनवाडी

उत्तर

22

निश्चित रूप से। ओह, आप LINQ प्रश्नों के साथ अंकगणित की जगह ले सकता:

http://blogs.msdn.com/ericlippert/archive/2009/12/07/query-transformations-are-syntactic.aspx

लेकिन तुम नहीं करना चाहिए।

एक क्वेरी अभिव्यक्ति का उद्देश्य एक क्वेरी ऑपरेशन का प्रतिनिधित्व करता है। "फॉर" लूप का उद्देश्य किसी विशेष कथन पर पुनरावृत्ति करना है ताकि इसके दुष्प्रभाव कई बार निष्पादित किए जा सकें। वे अक्सर बहुत अलग होते हैं। मैं उन लूपों को प्रतिस्थापित करने के लिए प्रोत्साहित करता हूं जिनके उद्देश्य केवल उच्च स्तरीय संरचनाओं के साथ डेटा पूछना है जो डेटा को अधिक स्पष्ट रूप से पूछताछ करते हैं। मैं क्वेरी समझ के साथ साइड-इफेक्ट-जनरेटिंग कोड को प्रतिस्थापित करने से दृढ़ता से हतोत्साहित करता हूं, हालांकि ऐसा करना संभव है।

+17

सबकुछ एक नाखून नहीं है ... – AwkwardCoder

+0

@Eric: क्या आप गैर-दुष्प्रभाव-उत्पन्न कोड निष्पादित करने के लिए ForEach एक्सटेंशन विधि का उपयोग करने में कुछ भी गलत देखते हैं उदा। क्वेरी से प्रत्येक परिणाम पर एक WriteLn कर रहे हैं? –

+2

@ टॉम: मैं यहां आपकी विचार की ट्रेन का पालन नहीं कर रहा हूं। निश्चित रूप से एक लाइन लिखना एक दुष्प्रभाव है, नहीं? –

2

तकनीकी तौर पर, हाँ।

कोई भी foreach लूप ForEach विस्तार विधि का उपयोग करके LINQ में परिवर्तित किया जा सकता है, जैसे MoreLinq में से एक।

आप केवल उपयोग करने के लिए "शुद्ध" LINQ (केवल अंतर्निहित विस्तार विधि) चाहते हैं, आप Aggregate विस्तार विधि का दुरुपयोग कर सकते हैं, इस तरह:

foreach(type item in collection { statements } 

type item; 
collection.Aggregate(true, (j, itemTemp) => { 
    item = itemTemp; 
    statements 
    return true; 
); 

यह सही ढंग से संभाल लेंगे किसी भी foreach लूप, यहां तक ​​कि जेरेडपार का जवाब भी। EDIT: जब तक यह ref/out पैरामीटर, असुरक्षित कोड या yield return का उपयोग नहीं करता है।
क्या आप वास्तविक कोड में इस चाल का उपयोग नहीं करते हैं। सामान्य हाँ में

///<summary>Appends a list of strings to a StringBuilder, separated by a separator string.</summary> 
///<param name="builder">The StringBuilder to append to.</param> 
///<param name="strings">The strings to append.</param> 
///<param name="separator">A string to append between the strings.</param> 
public static StringBuilder AppendJoin(this StringBuilder builder, IEnumerable<string> strings, string separator) { 
    if (builder == null) throw new ArgumentNullException("builder"); 
    if (strings == null) throw new ArgumentNullException("strings"); 
    if (separator == null) throw new ArgumentNullException("separator"); 

    bool first = true; 

    foreach (var str in strings) { 
     if (first) 
      first = false; 
     else 
      builder.Append(separator); 

     builder.Append(str); 
    } 

    return builder; 
} 

///<summary>Combines a collection of strings into a single string.</summary> 
public static string Join<T>(this IEnumerable<T> strings, string separator, Func<T, string> selector) { return strings.Select(selector).Join(separator); } 
///<summary>Combines a collection of strings into a single string.</summary> 
public static string Join(this IEnumerable<string> strings, string separator) { return new StringBuilder().AppendJoin(strings, separator).ToString(); } 
+0

वहाँ अपने कोड में एक ही LINQ क्वेरी नहीं है, इसलिए इसमें असली सवाल का जवाब नहीं कर रहा है यह है ? भाषा एकीकृत क्वेरी का अर्थ विस्तार विधियों का नहीं है :) यह विस्तार विधियों के आसपास बना है लेकिन यह भाषा विस्तारित क्वेरी सिंटैक्स –

+0

@Rune के प्रत्येक एक्सटेंशन विधि भाग को नहीं बनाता है: मुझे इसके बारे में अच्छी जानकारी है। हालांकि, यहां हर दूसरे उत्तर सहित सामान्य उपयोग, LINQ की शैली का पालन करने वाले किसी भी एक्सटेंशन विधियों को संदर्भित करता है। (उदाहरण के लिए, MoreLinq) – SLaks

13

, लेकिन वहाँ विशिष्ट मामलों है कि बेहद मुश्किल होता है इस प्रकार हैं:


अपने विशिष्ट मामले में, आप एक स्ट्रिंग Join विस्तार विधि, इसी प्रकार के प्रयोग करना चाहिए। उदाहरण के लिए, सामान्य मामले में निम्न कोड हैकिंग के अच्छे सौदे के बिना LINQ अभिव्यक्ति पर पोर्ट नहीं करता है।

var list = new List<Func<int>>(); 
foreach (var cur in (new int[] {1,2,3})) { 
    list.Add(() => cur); 
} 

कारण है कि यह है कि पाश के लिए एक साथ, यह कैसे यात्रा चर को बंद करने में कब्जा कर लिया है के दुष्प्रभाव को देखने के लिए संभव है। LINQ अभिव्यक्ति पुनरावृत्ति चर के आजीवन अर्थशास्त्र को छिपाते हैं और आपको इसके मूल्य को कैप्चर करने के दुष्प्रभावों को देखने से रोकते हैं।

नोट। उपर्युक्त कोड निम्न LINQ अभिव्यक्ति के बराबर नहीं है।

var list = Enumerable.Range(1,3).Select(x =>() => x).ToList(); 

foreach नमूना Func<int> वस्तुओं जो सभी लौट 3. LINQ संस्करण Func<int> जो क्रमश: 1,2 और 3 लौटने की एक सूची की एक सूची तैयार करता है। इस बंदरगाह को पकड़ने के लिए इस शैली को पकड़ने की शैली यही है।

+1

@ सेन, आपका काउंटर उदाहरण एक अलग परिणाम उत्पन्न करता है। मेरा नमूना 'Func ' की एक सूची तैयार करेगा जो सभी लौटाएगा 3. आपका 1,2 और 3. – JaredPar

+0

हाँ, अच्छा बिंदु लौटाएगा। हटाए गए। –

1

सामान्य तौर पर, आप एक प्रतिनिधि जो एक foreach चक्र के शरीर का प्रतिनिधित्व करता है का उपयोग कर एक लैम्ब्डा अभिव्यक्ति, की तरह अपने मामले में कुछ लिख सकते हैं:

resource => { if (sb.Length != 0) sb.Append(", "); sb.Append(resource.Id); } 

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

1

आपके प्रश्न में विशिष्ट पाश घोषणात्मक रूप से किया जा सकता है इस तरह:

var result = ListOfResources 
      .Select<Resource, string>(r => r.Id.ToString()) 
      .Aggregate<string, StringBuilder>(new StringBuilder(), (sb, s) => sb.Append(sb.Length > 0 ? ", " : String.Empty).Append(s)) 
      .ToString(); 

प्रदर्शन के रूप में, आप प्रदर्शन ड्रॉप की उम्मीद कर सकते हैं लेकिन यह अधिकांश अनुप्रयोगों के लिए स्वीकार्य है।

2

मुझे लगता है कि सबसे महत्वपूर्ण है कि यहाँ अर्थ भ्रम से बचने के है, अपने कोड केवल अल्पज्ञता कार्यात्मक होना चाहिए जब यह वास्तव में कार्यात्मक है। दूसरे शब्दों में, कृपया LINQ अभिव्यक्तियों में दुष्प्रभावों का उपयोग न करें।

+0

क्या आप इस तथ्य पर संकेत दे रहे हैं कि लूप के प्रत्येक आइटम के लिए किसी समकक्ष LINQ कथन को सभी वस्तुओं के लिए बिल्कुल वही करना चाहिए ... – AwkwardCoder

+0

नहीं, मैं बस इतना कह रहा हूं कि LINQ अभिव्यक्ति को "करने के रूप में नहीं सोचना चाहिए "कुछ भी, लेकिन कुछ" होने "के रूप में, यानी एक मूल्य है। यदि आपको इस तरह से सोचने का प्राकृतिक तरीका नहीं मिल रहा है, तो इसे LINQ अभिव्यक्ति के रूप में तैयार न करें। जैसा होता है, आपके द्वारा दिया गया विशिष्ट उदाहरण * एक कार्यात्मक अभिव्यक्ति के रूप में समझ में आता है, जैसा कि कोनराड ऊपर दिखाया गया है। संपादित करें: एर, प्रतीक्षा करें, तरह का। इसे "बिल्कुल वही" करना चाहिए क्योंकि यह स्टेटलेस होना चाहिए (चरों को अद्यतन करने वाले बयान पर निर्भर नहीं होना चाहिए), यदि आपका यही मतलब है। –

3

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

कार्यात्मक कोड यह बहुत आसान बनाता है, वास्तव में, क्योंकि इसे आपके कोड जैसे विशेष मामले की आवश्यकता नहीं होती है। अभी भी बेहतर है, .NET पहले से ही है इस विशेष कार्रवाई लागू है, और शायद अपने कोड 1) तुलना में अधिक कुशल:

return String.Join(", ", ListOfResources.Select(s => s.Id.ToString()).ToArray()); 

(हाँ, ToArray() करने के लिए कॉल से परेशान है, लेकिन Join एक बहुत पुरानी विधि है और । LINQ)

बेशक

, Join की एक "बेहतर" संस्करण इस तरह इस्तेमाल किया जा सकता से पहले:

return ListOfResources.Select(s => s.Id).Join(", "); 

कार्यान्वयन अपेक्षाकृत सरल है - लेकिन एक बार फिर, StringBuilder (प्रदर्शन के लिए) का उपयोग करना अनिवार्य बनाता है।

public static String Join<T>(this IEnumerable<T> items, String delimiter) { 
    if (items == null) 
     throw new ArgumentNullException("items"); 
    if (delimiter == null) 
     throw new ArgumentNullException("delimiter"); 

    var strings = items.Select(item => item.ToString()).ToList(); 
    if (strings.Count == 0) 
     return string.Empty; 

    int length = strings.Sum(str => str.Length) + 
       delimiter.Length * (strings.Count - 1); 
    var result = new StringBuilder(length); 

    bool first = true; 

    foreach (string str in strings) { 
     if (first) 
      first = false; 
     else 
      result.Append(delimiter); 
     result.Append(str); 
    } 

    return result.ToString(); 
} 

1) परावर्तक में कार्यान्वयन को देखा बिना, मुझे लगता है चाहते हैं कि String.Join तार पर एक पहले पास कुल लंबाई निर्धारित करने के लिए बनाता है। इसका उपयोग StringBuilder तदनुसार प्रारंभ करने के लिए किया जा सकता है, इस प्रकार बाद में महंगा प्रति संचालन को बचाया जा सकता है।

संपादित SLaks द्वारा: यहाँ नेट 3.5 से String.Join के संबंधित भाग के लिए संदर्भ स्रोत है:

string jointString = FastAllocateString(jointLength); 
fixed (char * pointerToJointString = &jointString.m_firstChar) { 
    UnSafeCharBuffer charBuffer = new UnSafeCharBuffer(pointerToJointString, jointLength); 

    // Append the first string first and then append each following string prefixed by the separator. 
    charBuffer.AppendString(value[startIndex]); 
    for (int stringToJoinIndex = startIndex + 1; stringToJoinIndex <= endIndex; stringToJoinIndex++) { 
     charBuffer.AppendString(separator); 
     charBuffer.AppendString(value[stringToJoinIndex]); 
    } 
    BCLDebug.Assert(*(pointerToJointString + charBuffer.Length) == '\0', "String must be null-terminated!"); 
} 
+1

+1। एक स्पष्ट, संक्षिप्त तरीके से इस विशिष्ट स्थिति को संभालने का एकमात्र तरीका है। – jeroenh

+2

.NET4 में, 'स्ट्रिंग.जॉइन' भी एक आईनेमरेबल लेता है :) – JulianR

+1

@ जुलिएन: यह सुनकर अच्छा लगा। *आखिरकार*! –

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