2012-02-07 8 views
23

जो मैंने पढ़ा है, उससे कुछ संग्रहों के एन्यूमरेटर प्रकारों के प्रदर्शन कारणों के संदर्भ संदर्भों के बजाय उत्परिवर्तनीय structs होने के लिए एक डिज़ाइन निर्णय लिया गया था। सूची। उपभोक्ता सबसे प्रसिद्ध है।सी # Arrays गणना के लिए संदर्भ प्रकार का उपयोग क्यों करते हैं, लेकिन सूची <T> एक परिवर्तनीय संरचना का उपयोग करता है?

मैं कुछ पुराने कोड की जांच कर रहा था जो सरणी का उपयोग करते थे, और यह आश्चर्यचकित हुआ कि सी # Arrays प्रकार के SZGenericArrayEnumerator को उनके जेनेरिक एन्यूमरेटर प्रकार के रूप में वापस लौटाते हैं, जो एक संदर्भ प्रकार है।

मुझे आश्चर्य है कि अगर कोई जानता है कि क्यों ऐरे के जेनेरिक इटरेटर को संदर्भ प्रकार के रूप में लागू किया गया था, तो कई अन्य प्रदर्शन महत्वपूर्ण संग्रहों ने इसके बजाय उत्परिवर्तनीय structs का उपयोग किया।

उत्तर

36

जो मैंने पढ़ा है, उससे कुछ कारणों के प्रदर्शन के कारण संदर्भ संग्रहों के बजाय कुछ संग्रहों के गणक प्रकारों के लिए एक डिज़ाइन निर्णय किया गया था।

अच्छा सवाल।

सबसे पहले, आप सही हैं। हालांकि सामान्य रूप से, परिवर्तनीय मूल्य प्रकार खराब कोड गंध हैं, इस मामले में वे उचित हैं:

  • उत्परिवर्तन लगभग पूरी तरह से उपयोगकर्ता से छुपा हुआ है।
  • यह बेहद असंभव है कि कोई भी भ्रमित तरीके से गणनाकर्ता का उपयोग करने जा रहा है।
  • एक परिवर्तनीय मूल्य प्रकार का उपयोग वास्तव में एक बेहद आम परिदृश्य में यथार्थवादी प्रदर्शन समस्या को हल करता है।

मैं किसी को जानता है, तो जब बहुत से अन्य प्रदर्शन महत्वपूर्ण संग्रह के बजाय अस्थायी structs इस्तेमाल किया क्यों सरणी के सामान्य इटरेटर एक संदर्भ प्रकार के रूप में लागू किया गया था सोच रहा हूँ।

क्योंकि आप व्यक्ति की तरह है जो एक सरणी तो गणना कारण है कि आप पहली जगह में एक प्रगणक उपयोग कर रहे हैं के प्रदर्शन के बारे में चिंतित है कर रहे हैं? स्वर्ग के लिए यह सरणी है; बस एक for लूप लिखें जो एक सामान्य व्यक्ति की तरह अपनी नीतियों पर निर्भर करता है और कभी गणनाकर्ता आवंटित नहीं करता है। (या एक foreach पाश, यदि यह जानता है कि लूप संग्रह एक सरणी है सी # संकलक बराबर for लूप में foreach पाश पुनर्लेखन जाएगा।)

केवल कारण है कि आप में एक सरणी से एक प्रगणक प्राप्त होता पहली जगह यह है कि यदि आप इसे IEnumerator<T> पर लेते हैं, तो यदि अंकुआ एक संरचना है तो आप इसे बॉक्सिंग करने जा रहे हैं, फिर भी पर जा रहे हैं। मूल्य प्रकार बनाने और फिर मुक्केबाजी करने की कीमत क्यों लेते हैं? बस इसे शुरू करने के लिए एक संदर्भ प्रकार बनाओ।

+0

LINQ के प्रसार (और अभिव्यक्तित्मक शक्ति) को देखते हुए, गणकों को अक्सर सरणी के लिए पुनर्प्राप्त किया जा रहा है। उदाहरण, कई गुणों/विधियों के लिए .NET रिटर्न सरणी में प्रतिबिंब वर्ग - और LINQ डब्ल्यू/प्रतिबिंब का उपयोग काफी आसान है। मेरे ज्ञान के लिए, अधिकांश LINQ ऑपरेटर यह निर्धारित करने के लिए विशेष केस चेक नहीं करते हैं कि क्या IENumerable <> वे वास्तव में एक सरणी हैं। – LBushkin

+0

@LBushkin: सही। यदि आप आवंटित ढेर की लागत लेने के लिए तैयार नहीं हैं और फिर कचरा * * गणनाकर्ता * इकट्ठा कर रहे हैं तो आप संभावित रूप से ढेर आवंटन और कचरा इकट्ठा करने की लागत * क्वेरी *, और इसके द्वारा बनाई गई सभी गणनाकर्ता वस्तुओं की लागत को लेने के लिए अनिच्छुक रूप से अनिच्छुक हैं , और आपके द्वारा बनाए जाने वाले सभी प्रतिनिधियों को। उस क्वेरी को कॉल करने के सभी ओवरहेड का उल्लेख नहीं करना (सही होने के लिए तर्क जांचना, और इसी तरह)। वस्तुओं के लिए LINQ उचित रूप से प्रदर्शन करने वाला है लेकिन यह निश्चित रूप से ढेर आवंटन को कम करने के लिए डिज़ाइन नहीं किया गया था! LINQ जगह पर ढेर स्मृति आवंटित करता है। –

+0

एक अन्य कारक, मुझे संदेह है कि मूल 'सूची' के लिए गणनाकर्ता जेनेरिक से पहले के दिनों में डिजाइन किया गया था। 'List.Enumerator' की संरचना-नस्ल केवल तीन प्रकार के कोड को प्रभावित करती है: कोड जो स्पष्ट रूप से' List.Enumerator' प्रकार का उपयोग करता है, कोड जो 'जेन्यूमेरेबल' से बाध्य एक सामान्य पैरामीटर स्वीकार करता है, या कोड जो 'var' का उपयोग करने के लिए' var' का उपयोग करता है चर जो एक सूची के गणनाकर्ता होगा। यदि कोई प्रोग्रामर 'List.Enumerator' निर्दिष्ट करने जा रहा है, तो प्रोग्रामर को पता होना चाहिए कि वह क्या कर रहा है। अन्य दो प्रकार के कोड के लिए, जब वे 'List.Enumerator' डिज़ाइन किया गया था तब वे अस्तित्व में नहीं थे। – supercat

2

Arrays सी # कंपाइलर में कुछ विशेष उपचार प्राप्त करते हैं। जब आप foreach का उपयोग करते हैं, तो कंपाइलर इसे for लूप में अनुवाद करता है। तो struct गणक का उपयोग करने में कोई प्रदर्शन लाभ नहीं है।

List<T> दूसरी ओर किसी विशेष उपचार के बिना एक सादा वर्ग है, इसलिए बेहतर प्रदर्शन में संरचना परिणामों का उपयोग करना।

+0

लूप के लिए सभी foreach संकलित नहीं है? –

+1

@OskarKjellin no, सामान्य 'foreach' loops 'while (enumerator.MoveNext()) {var element = enumerator.Current; ...}' जबकि 'foreach' सरणी में सरणी अनुक्रमणिका पर लूप' (' के लिए (int i = 0; i CodesInChaos

+6

@OskarKjellin: सभी 'foreach' और' for' loops * अंततः * loops में संकलित होते हैं। CodeInChaos का बिंदु यह है कि सरणी पर 'foreach' loops हैं वास्तव में पारंपरिक 'के लिए पहले' int (int i = 0; i

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

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