सी # सुधार?


9

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

क्या यह देखने वाले कीवर्ड के उपयोग के खिलाफ जाता है कि कंपाइलर इस कीवर्ड को कहीं भी उपयोग करने की अनुमति नहीं देगा लेकिन लूप निर्माण में कैसे होगा?


7
आप इसे एक विस्तार विधि के साथ स्वयं कर सकते हैं, डान फिंच का जवाब देखें: stackoverflow.com/a/521894/866172 (सर्वश्रेष्ठ मूल्यांकित उत्तर नहीं है, लेकिन मुझे लगता है कि यह "क्लीनर" है)
जालन

$ _ जैसे पर्ल और सामान दिमाग में आता है। मुझे उस सामान से नफरत है।
यम मारकोविच

2
C # के बारे में मैं जो कुछ भी जानता हूं, उसमें कई संदर्भ-आधारित कीवर्ड हैं, इसलिए यह "कीवर्ड के उपयोग के खिलाफ नहीं" जाएगा। जैसा कि नीचे एक उत्तर में बताया गया है, आप शायद सिर्फ एक for () लूप का उपयोग करना चाहते हैं ।
क्रेग

@ जयलयन कृपया जवाब के रूप में पोस्ट करें। यह सबसे अच्छे तरीकों में से एक है।
अपूर्व खुरसिया

@MonsterTruck: मुझे नहीं लगता कि उसे चाहिए। वास्तविक समाधान इस प्रश्न को SO को माइग्रेट करना होगा और इसे जलएएन लिंक के प्रश्न के डुप्लिकेट के रूप में बंद करना होगा।
ब्रायन

जवाबों:


54

यदि आपको एक लूप के अंदर लूप काउंट की आवश्यकता है foreachतो आप सिर्फ एक forलूप का उपयोग क्यों नहीं करते हैं । foreachलूप के विशिष्ट उपयोगों बनाने का इरादा था forसरल छोरों। ऐसा लगता है कि आपके पास एक ऐसी स्थिति है जहां की सादगी foreachअब फायदेमंद नहीं है।


1
+1 - अच्छी बात। यह IEnumerable.Count या object.length का उपयोग करने के लिए पर्याप्त है। लूप के लिए नियमित रूप से ऑब्जेक्ट में तत्वों की संख्या का पता लगाने के साथ किसी भी समस्या से बचने के लिए।

11
@ GlenH7: कार्यान्वयन पर निर्भर करता है, IEnumerable.Countमहंगा हो सकता है (या असंभव है, अगर यह एक बार केवल गणना है)। आपको यह जानने की जरूरत है कि सुरक्षित होने से पहले अंतर्निहित स्रोत क्या है।
बाइनरी वॉरियर

2
-1: में विधेयक वैगनर अधिक प्रभावी सी # का वर्णन करता है क्यों एक हमेशा से चिपक जाना चाहिए foreachसे अधिक for (int i = 0…)। उन्होंने एक दो पृष्ठ लिखे हैं, लेकिन मैं दो शब्दों में संक्षेपित कर सकता हूं - पुनरावृत्ति पर संगणक अनुकूलन। ठीक है कि दो शब्द नहीं थे।
अपूर्व खुरसिया

13
@MonsterTruck: बिल वैगनर ने जो कुछ भी लिखा है, मैंने ओपी द्वारा वर्णित एक जैसे पर्याप्त परिस्थितियों को देखा है, जहां एक forलूप बेहतर पठनीय कोड देता है (और सैद्धांतिक संकलक अनुकूलन अक्सर समय से पहले अनुकूलन होता है)।
डॉक ब्राउन

1
@DocBrown यह समयपूर्व अनुकूलन के रूप में सरल नहीं है। मैंने खुद अतीत में आने वाली अड़चनों की पहचान की है और उन्हें इस दृष्टिकोण का उपयोग करके ठीक किया है (लेकिन मैं मानता हूं कि मैं संग्रह में हजारों वस्तुओं के साथ काम कर रहा था)। जल्द ही एक कार्य उदाहरण पोस्ट करने की उम्मीद है। इस बीच, यहाँ जॉन स्कीट है । [उनका कहना है कि प्रदर्शन में सुधार महत्वपूर्ण नहीं होगा लेकिन यह संग्रह और वस्तुओं की संख्या पर निर्भर करता है]।
अपूर्व खुरसिया

37

आप इस तरह से गुमनाम प्रकारों का उपयोग कर सकते हैं

foreach (var item in items.Select((v, i) => new { Value = v, Index = i }))
{
    Console.WriteLine("Item value is {0} and index is {1}.", item.Value, item.Index);
}

यह पायथन के enumerateकार्य के समान है

for i, v in enumerate(items):
    print v, i

+1 ओह ... प्रकाश बल्ब। मुझे वह तरीका पसंद है। मैंने इसके बारे में पहले कभी क्यों नहीं सोचा?
फिल

17
जो कोई लूप के लिए शास्त्रीय रूप से C # में इसे पसंद करता है, उसकी स्पष्ट रूप से पठनीयता पर एक अजीब राय है। यह एक साफ-सुथरी चाल हो सकती है लेकिन मैं उत्पादन गुणवत्ता कोड में ऐसा कुछ नहीं होने दूंगा। यह लूप के लिए एक शास्त्रीय की तुलना में बहुत अधिक शोर है और इसे तेजी से नहीं समझा जा सकता है।
फाल्कन

4
@ फाल्कन, यह एक वैध विकल्प है यदि आप लूप के लिए एक पुराने फैशन का उपयोग नहीं कर सकते हैं (जिसे गणना की आवश्यकता है)। यह कुछ ऑब्जेक्ट का संग्रह है, जिसे मुझे चारों ओर काम करना था ...
फैब्रिकियो आरुजो

8
@FabricioAraujo: निश्चित रूप से लूप के लिए C # को एक गिनती की आवश्यकता नहीं है। जहां तक ​​मुझे पता है कि वे लूप्स के लिए सी के समान ही हैं, जिसका अर्थ है कि आप लूप को समाप्त करने के लिए किसी भी बूलियन मान का उपयोग कर सकते हैं। मूवमेंट के अंत के लिए चेक के रूप में जब MoveNext झूठे हो जाते हैं।
ज़ैन लिंक्स

1
यह सभी कोड होने का फ़ायदा है कि इसे खोजने के बजाय फ़ॉरच ब्लॉक की शुरुआत में वृद्धि को संभालने के लिए सभी कोड होने चाहिए।
जेएफओ

13

मैं केवल अनुरोध के अनुसार डैन फिंच का उत्तर यहां जोड़ रहा हूं ।

मुझे अंक न दें, डैन फिंच को अंक दें :-)

समाधान निम्नलिखित जेनेरिक एक्सटेंशन विधि लिखने के लिए है:

public static void Each<T>( this IEnumerable<T> ie, Action<T, int> action )
{
    var i = 0;
    foreach ( var e in ie ) action( e, i++ );
}

जो इस तरह प्रयोग किया जाता है:

var strings = new List<string>();
strings.Each( ( str, n ) =>
{
    // hooray
} );

2
यह बहुत चालाक है, लेकिन मुझे लगता है कि इसके लिए एक सामान्य का उपयोग करना अधिक "पठनीय" है, खासकर अगर नीचे सड़क पर कोई व्यक्ति जो कोड को बनाए रखता है, वह .NET में इक्का के रूप में ज्यादा नहीं हो सकता है और विस्तार विधियों की अवधारणा से भी परिचित नहीं है। । मैं अभी भी कोड के इस छोटे से ले रहा हूँ, हालांकि। :)
मेटलमेस्टर

1
इस के दोनों ओर बहस कर सकता है, और मूल प्रश्न - मैं पोस्टर के इरादे से सहमत हूं, ऐसे मामले हैं जहां फ़ॉरच बेहतर पढ़ता है, लेकिन एक सूचकांक की आवश्यकता है, फिर भी मैं हमेशा एक भाषा में अधिक संश्लिष्ट चीनी जोड़ने के लिए प्रतिकूल हूं यदि यह नहीं है बुरी तरह से जरूरत है - मैं बिल्कुल अलग नाम के साथ अपने कोड में एक ही समाधान है - strings.ApplyIndexed <T> (......)
मार्क Mullin

मैं मार्क मुलिन से सहमत हूं, आप दोनों तरफ बहस कर सकते हैं। मैंने सोचा कि यह विस्तार के तरीकों का एक बहुत ही चतुर उपयोग था और इसने इस सवाल को फिट किया इसलिए मैंने इसे साझा किया।
जलयन् १४'१२

5

मैं इस बारे में गलत हो सकता हूं, लेकिन मैंने हमेशा सोचा था कि फॉरच लूप का मुख्य बिंदु सी ++ के एसटीएल दिनों से पुनरावृत्ति को सरल बनाना था।

for(std::stltype<typename>::iterator iter = stl_type_instance.begin(); iter != stl_type_instance.end(); iter++)
{
   //dereference iter to use each object.
}

.NET में IEnumerable के .NET के उपयोग से इसकी तुलना करें

foreach(TypeName instance in DescendentOfIEnumerable)
{
   //use instance to use the object.
}

उत्तरार्द्ध बहुत सरल है और कई पुराने नुकसानों से बचा जाता है। इसके अलावा, वे लगभग समान नियमों का पालन करते हैं। उदाहरण के लिए, आप लूप के अंदर IEnumerable सामग्री को नहीं बदल सकते हैं (जो बहुत अधिक समझ में आता है अगर आप इसे STL के बारे में सोचते हैं)। हालांकि, लूप के लिए अक्सर पुनरावृत्ति की तुलना में एक अलग तार्किक उद्देश्य होता है।


4
+1: कुछ लोगों को याद है कि मूल रूप से इटरेटर पैटर्न पर फॉर्च्यूनिक शुगर है।
स्टीवन एवर्स

1
वैसे कुछ अंतर हैं; .NET में मौजूद पॉइंटर हेरफेर प्रोग्रामर से बहुत गहराई से छिपा हुआ है, लेकिन आप एक लूप के लिए लिख सकते हैं जो C ++ उदाहरण की तरह for(IEnumerator e=collection.GetEnumerator;e.MoveNext();) { ... }
दिखता है

@KeithS, यदि आपको एहसास नहीं है कि आप जो कुछ भी .NET में छूते हैं, वह एक संरचित प्रकार IS A POINTER नहीं है, बस अलग-अलग सिंटैक्स के साथ (- -> के बजाय)। वास्तव में, एक संदर्भ प्रकार को किसी विधि में पास करना एक संकेतक के रूप में इसे पारित करने और इसे संदर्भ द्वारा पारित करने के समान ही है, इसे एक दोहरे ट्रांसमीटर के रूप में पारित कर रहा है। वही चीज। कम से कम, यह वह तरीका है जो एक व्यक्ति की मूल भाषा है C ++ इसके बारे में सोचता है। तरह तरह, आप स्कूल में स्पैनिश सीखते हैं, यह सोचकर कि भाषा अपनी मूल भाषा से कैसे संबंधित है।
जोनाथन हेंसन

* मूल्य प्रकार संरचित प्रकार नहीं। माफ़ करना।
जोनाथन हेंसन

2

यदि प्रश्न में संग्रह एक है IList<T>, तो आप केवल forअनुक्रमण के साथ उपयोग कर सकते हैं :

for (int i = 0; i < collection.Count; i++)
{
    var item = collection[i];
    // …
}

यदि नहीं, तो आप उपयोग कर सकते हैं Select(), इसमें एक अधिभार है जो आपको सूचकांक देता है।

अगर यह भी उपयुक्त नहीं है, तो मुझे लगता है कि मैन्युअल रूप से सूचकांक को बनाए रखना काफी सरल है, यह कोड की सिर्फ दो बहुत छोटी लाइनें हैं। इसके लिए विशेष रूप से एक कीवर्ड बनाना एक ओवरकिल होगा।

int i = 0;
foreach (var item in collection)
{
    // …
    i++;
}

+1, यह भी, यदि आप लूप के अंदर एक बार इंडेक्स का उपयोग कर रहे हैं, तो आप कुछ ऐसा कर सकते हैं foo(++i)या foo(i++)उस पर निर्भर करते हैं, जिस पर इंडेक्स की आवश्यकता है।
जॉब

2

यदि आप सभी जानते हैं कि संग्रह एक IEnumerable है, लेकिन आपको अभी तक कितने तत्वों को संसाधित करने का ट्रैक रखने की आवश्यकता है (और इस तरह कुल जब आप कर रहे हैं), तो आप लूप के लिए एक मूल के लिए एक जोड़ी लाइनें जोड़ सकते हैं:

var coll = GetMyCollectionAsAnIEnumerable();
var idx = 0;
for(var e = coll.GetEnumerator(); e.MoveNext(); idx++)
{
   var elem = e.Current;

   //use elem and idx as you please
}

आप फ़र्क़ करने के लिए बढ़े हुए इंडेक्स वेरिएबल को भी जोड़ सकते हैं:

var i=0;
foreach(var elem in coll)
{
   //do your thing, then...
   i++;
}

यदि आप इसे अधिक सुंदर बनाना चाहते हैं, तो आप इन विवरणों को "छिपाने" के लिए एक विस्तार विधि या दो को परिभाषित कर सकते हैं:

public static void ForEach<T>(this IEnumerable<T> input, Action<T> action)
{
    foreach(T elem in input)
        action(elem);
}

public static void ForEach<T>(this IEnumerable<T> input, Action<T, int> action)
{
    var idx = 0;
    foreach(T elem in input)
        action(elem, idx++); //post-increment happens after parameter-passing
}

//usage of the index-supporting method
coll.ForEach((e, i)=>Console.WriteLine("Element " + (i+1) + ": " + e.ToString()));

1

यदि आप अन्य नामों के साथ टकराव के ब्लॉक को साफ रखना चाहते हैं तो स्कोपिंग भी काम करती है ...

{ int index = 0; foreach(var el in List) { Console.WriteLine(el + " @ " + index++); } }

-1

इसके लिए मैं एक whileलूप का उपयोग करता हूं । लूप्स forभी काम करते हैं, और कई लोग उन्हें पसंद करने लगते हैं, लेकिन मैं केवल forऐसी चीज़ के साथ उपयोग करना पसंद करता हूं, जिसमें निश्चित संख्या में पुनरावृत्तियों होंगे। IEnumerables रन टाइम पर उत्पन्न हो सकता है, इसलिए, मेरे दिमाग में, whileअधिक समझ में आता है। यदि आप चाहें, तो इसे एक अनौपचारिक कोडिंग गाइडलाइन कहें।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.