C # में संरचना और वर्ग अलग-अलग अवधारणाएं क्यों हैं?


44

C # में प्रोग्रामिंग करते समय, मैंने एक अजीब भाषा डिजाइन निर्णय पर ठोकर खाई, जिसे मैं अभी समझ नहीं पाया।

तो, सी # (और सीएलआर) के दो समग्र डेटा प्रकार हैं: struct(मूल्य-प्रकार, स्टैक पर संग्रहीत, कोई उत्तराधिकार नहीं) और class(संदर्भ-प्रकार, ढेर पर संग्रहीत, विरासत है)।

यह सेटअप पहले अच्छा लगता है, लेकिन फिर आप किसी एग्रीगेट प्रकार के पैरामीटर को लेने के तरीके पर ठोकर खाते हैं, और यह पता लगाने के लिए कि क्या यह वास्तव में वैल्यू टाइप का है या किसी रेफरेंस टाइप का है, आपको इसके टाइप का डिक्लेरेशन ढूंढना होगा। यह कई बार वास्तव में भ्रमित कर सकता है।

इस समस्या का आम तौर पर स्वीकृत समाधान संभव गलतियों को रोकने के लिए सभी structs को "अपरिवर्तनीय" (अपने क्षेत्र की स्थापना readonly) के रूप में घोषित कर रहा है, जो structएस की उपयोगिता को सीमित करता है।

C ++, उदाहरण के लिए, एक बहुत अधिक प्रयोग करने योग्य मॉडल को नियोजित करता है: यह आपको स्टैक पर या हीप पर ऑब्जेक्ट इंस्टेंस बनाने की अनुमति देता है और इसे मान या संदर्भ (या पॉइंटर द्वारा) पास करता है। मैं सुनता रहता हूं कि C # C ++ से प्रेरित था, और मैं अभी यह नहीं समझ पा रहा हूं कि इस एक तकनीक को क्यों नहीं लिया। दो अलग-अलग आवंटन विकल्पों (हीप और स्टैक) के साथ संयोजन में classऔर structएक निर्माण में और उन्हें मूल्यों के रूप में या (स्पष्ट रूप से) के माध्यम से पारित करना refऔर outकीवर्ड के माध्यम से संदर्भ एक अच्छी बात की तरह लगते हैं।

सवाल यह है कि दो आवंटन विकल्पों के साथ एक समग्र प्रकार के बजाय C # और CLR में अलग-अलग अवधारणाएँ क्यों classऔर कैसे structबन गईं?


34
"आम तौर पर समस्या के लिए स्वीकृत समाधान सभी संरचनाओं को" अपरिवर्तनीय "घोषित करने लगता है ... संरचना की उपयोगिता को सीमित करना" बहुत से लोग यह तर्क देंगे कि किसी भी चीज़ को अपरिवर्तनीय बनाना आमतौर पर इसे अधिक उपयोगी बनाता है जब भी यह प्रदर्शन अड़चन का कारण नहीं होता है। इसके अलावा, structहमेशा स्टैक पर संग्रहीत नहीं किया जाता है; किसी structफ़ील्ड के साथ ऑब्जेक्ट पर विचार करें । एक तरफ, जैसा कि मेसन व्हीलर ने स्लाइसिंग समस्या का उल्लेख किया है, शायद सबसे बड़ा कारण है।
डोभाल

7
यह सच नहीं है कि C # C ++ से प्रेरित था; बल्कि C # सभी C से प्रेरित था (अच्छी तरह से मतलब और उस समय अच्छा लग रहा था) C ++ और Java दोनों के डिज़ाइन में गलतियाँ।
पीटर जार्जेंस

18
नोट: स्टैक और हीप कार्यान्वयन विवरण हैं। ऐसा कुछ भी नहीं है जो कहता है कि स्टैक पर स्ट्रक्चर इंस्टेंसेस को आवंटित किया जाना है और क्लास इंस्टेंसेस को ढेर पर आवंटित किया जाना है। और यह वास्तव में सच भी नहीं है। उदाहरण के लिए, यह बहुत अच्छी तरह से संभव है कि एक कंपाइलर एस्केप एनालिसिस का उपयोग करके निर्धारित कर सकता है कि एक चर स्थानीय दायरे से बच नहीं सकता है और इस तरह इसे स्टैक पर आवंटित कर सकता है, भले ही यह एक वर्ग उदाहरण हो। यह भी नहीं कहा गया है कि एक स्टैक या ढेर होना चाहिए। आप ढेर पर एक लिंक की गई सूची के रूप में पर्यावरण फ्रेम आवंटित कर सकते हैं और यहां तक ​​कि एक स्टैक भी नहीं है।
जोर्ग डब्ल्यू मित्तग


3
"लेकिन फिर आप एक एग्रीगेट प्रकार के पैरामीटर को लेने के तरीके पर ठोकर खाते हैं, और यह पता लगाने के लिए कि क्या यह वास्तव में एक वैल्यू टाइप का है या एक रेफरेंस टाइप का है, आपको इसके टाइप का डिक्लेरेशन ढूंढना होगा" उम्म, वह बात क्यों, बिल्कुल ? विधि आपको एक मान पास करने के लिए कहती है। आप एक मान पास करते हैं। आपको इस बात पर ध्यान देना होगा कि यह संदर्भ प्रकार है या मूल्य प्रकार?
लुआण

जवाबों:


58

कारण C # (और जावा और अनिवार्य रूप से C ++ के बाद विकसित हर दूसरी OO भाषा) ने C ++ के मॉडल को इस पहलू में कॉपी नहीं किया है, क्योंकि C ++ जिस तरह से करता है वह एक भयावह गड़बड़ है।

आपने उपरोक्त प्रासंगिक बिंदुओं की सही पहचान की है struct: मूल्य प्रकार, कोई उत्तराधिकार नहीं। class: संदर्भ प्रकार, विरासत है। विरासत और मूल्य प्रकार (या अधिक विशेष रूप से, बहुरूपता और पास-दर-मूल्य) मिश्रण नहीं करते हैं; यदि आप किसी प्रकार के ऑब्जेक्ट को टाइप Derivedकरने के तरीके पर पास करते हैं Base, और फिर उस पर वर्चुअल विधि कहते हैं, तो उचित व्यवहार प्राप्त करने का एकमात्र तरीका यह सुनिश्चित करना है कि जो पास हुआ वह एक संदर्भ था।

उस और सभी अन्य गड़बड़ियों के बीच, जो आप सी ++ में निहित हैं, जिसमें मूल्य प्रकार (कॉपी कंस्ट्रक्टर और ऑब्जेक्ट स्लाइसिंग मन में आते हैं!) का सबसे अच्छा समाधान जस्ट सै नो है।

अच्छी भाषा डिजाइन सिर्फ सुविधाओं को लागू नहीं कर रहा है, यह भी जान रहा है कि किन सुविधाओं को लागू नहीं करना है, और ऐसा करने का सबसे अच्छा तरीका उन लोगों की गलतियों से सीखना है जो आपके सामने आए थे।


29
यह अभी तक C ++ पर एक और व्यर्थ व्यक्तिपरक शेख़ी है। नीचे नहीं जा सकता, लेकिन अगर मैं कर सकता था।
बार्टेक बानचेविकेज़

17
@MasonWheeler: " एक भयावह गड़बड़ है " पर्याप्त व्यक्तिपरक लगता है। यह पहले से ही एक लंबे टिप्पणी धागे में आपके दूसरे जवाब पर चर्चा की गई थी ; थ्रेड नुकीला हुआ (दुर्भाग्य से, क्योंकि इसमें उपयोगी टिप्पणियां शामिल थीं हालांकि लौ युद्ध सॉस में)। मुझे नहीं लगता कि यह पूरी बात यहाँ दोहराने के लायक है, लेकिन "C # ने इसे सही पाया और C ++ ने इसे गलत पाया" (जो आपको संदेश देने की कोशिश कर रहा है लगता है) वास्तव में एक व्यक्तिपरक कथन है।
एंडी प्रोल

15
@MasonWheeler: मैंने उस धागे में किया था जिसे नुक्कड़ किया गया था, और इसी तरह कई अन्य लोगों ने भी - जिसके कारण मुझे लगता है कि यह दुर्भाग्यपूर्ण है कि इसे हटा दिया गया। मुझे नहीं लगता कि उस थ्रेड को दोहराने के लिए यह एक अच्छा विचार है, लेकिन संक्षिप्त संस्करण है: C ++ में टाइप का उपयोगकर्ता , इसका डिजाइनर नहीं, यह तय करने के लिए मिलता है कि किस प्रकार के शब्दार्थ का उपयोग किया जाना चाहिए (संदर्भ शब्दार्थ या मूल्य अर्थ विज्ञान)। इसके फायदे और नुकसान हैं: आप पेशेवरों के विचार (या जानने के बिना) के खिलाफ क्रोध करते हैं। इसलिए विश्लेषण व्यक्तिपरक है।
एंडी प्रोल

7
आह मेरा मानना है कि LSP उल्लंघन चर्चा पहले से ही हो चुकी है। और मुझे विश्वास है कि अधिकांश लोगों ने सहमति व्यक्त की कि एलएसपी का उल्लेख बहुत अजीब और असंबंधित है, लेकिन यह जांच नहीं कर सकता क्योंकि एक मॉड ने टिप्पणी धागे को nuked किया
बार्टेक बानचेविच

9
यदि आप अपने अंतिम पैराग्राफ को शीर्ष पर ले जाते हैं और वर्तमान पहला पैराग्राफ हटाते हैं तो मुझे लगता है कि आपके पास सही तर्क है। लेकिन वर्तमान पहला पैराग्राफ केवल व्यक्तिपरक है।
मार्टिन यॉर्क

19

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

सी ++, तुलना करके, न केवल समायोज्य रिंच और सरौता शामिल हैं, बल्कि कुछ अजीब-बॉल विशेष उद्देश्य उपकरण हैं, जिनका उद्देश्य तुरंत स्पष्ट नहीं है, और यदि आप उन्हें रखने का सही तरीका नहीं जानते हैं, तो वे आसानी से आपका हिस्सा काट सकते हैं अंगूठे (लेकिन एक बार जब आप समझते हैं कि उनका उपयोग कैसे किया जाए, तो वे चीजें कर सकते हैं जो मूल उपकरण C # टूलबॉक्स में अनिवार्य रूप से असंभव हैं)। इसके अलावा, इसमें एक खराद, मिलिंग मशीन, सतह की चक्की, धातु-काटने वाले बैंड-आरा, आदि हैं, जो आपको किसी भी समय आपको डिजाइन करने और पूरी तरह से नए उपकरण बनाने की आवश्यकता महसूस करने के लिए (लेकिन हाँ, उन मशीनों के उपकरण के कारण और हो सकते हैं) गंभीर चोटें अगर आपको नहीं पता कि आप उनके साथ क्या कर रहे हैं - या भले ही आप लापरवाह हो जाएं)।

यह दर्शन में मूल अंतर को दर्शाता है: C ++ आपको उन सभी उपकरणों को देने का प्रयास करता है जो आपको आवश्यक रूप से किसी भी डिजाइन की आवश्यकता हो सकती है। यह नियंत्रित करने का कोई प्रयास नहीं करता है कि आप उन उपकरणों का उपयोग कैसे करते हैं, इसलिए उन डिजाइनों का उपयोग करना भी आसान है जो केवल दुर्लभ परिस्थितियों में अच्छी तरह से काम करते हैं, साथ ही ऐसे डिजाइन जो शायद सिर्फ एक घटिया विचार हैं और किसी को भी ऐसी स्थिति के बारे में नहीं पता है जिसमें वे अच्छी तरह से काम करने की संभावना रखते हैं। विशेष रूप से, इसका एक बड़ा सौदा डिजाइन के फैसलों को डिकम्प्लिंग करके किया जाता है - यहां तक ​​कि उन अभ्यासों में जो वास्तव में लगभग हमेशा युग्मित होते हैं। परिणामस्वरूप, केवल C ++ लिखने और C ++ अच्छी तरह से लिखने के बीच बहुत बड़ा अंतर है। C ++ अच्छी तरह से लिखने के लिए, आपको बहुत सारे मुहावरे और अंगूठे के नियमों को जानने की आवश्यकता है (अंगूठे के अन्य नियमों को तोड़ने के लिए गंभीरता से पुनर्विचार करने के बारे में अंगूठे के नियमों सहित)। नतीजतन, C ++ सीखने में आसानी की तुलना में उपयोग में आसानी (विशेषज्ञों द्वारा) की ओर अधिक उन्मुख है। ऐसी (सभी बहुत सारी) परिस्थितियां भी हैं जिनमें वास्तव में उपयोग करना बहुत आसान नहीं है।

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

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

इसलिए विशेष रूप से इस सवाल पर कि वे दो भाषाओं में कैसे classऔर structकैसे हैं: एक विरासत पदानुक्रम में बनाई गई वस्तुएं जहां आप अपने बेस क्लास / इंटरफ़ेस की आड़ में एक व्युत्पन्न वर्ग की वस्तु का उपयोग कर सकते हैं, आप बहुत अधिक हैं इस तथ्य के साथ अटक जाना कि आपको सामान्य रूप से किसी प्रकार के सूचक या संदर्भ के माध्यम से ऐसा करने की आवश्यकता है - एक ठोस स्तर पर, क्या होता है कि व्युत्पन्न वर्ग की वस्तु में कुछ मेमोरी होती है जिसे आधार वर्ग के उदाहरण के रूप में माना जा सकता है / इंटरफ़ेस, और व्युत्पन्न वस्तु को स्मृति के उस हिस्से के पते के माध्यम से हेरफेर किया जाता है।

C ++ में, यह प्रोग्रामर पर निर्भर है कि वह सही ढंग से - जब वह विरासत का उपयोग कर रहा है, तो यह सुनिश्चित करने के लिए उसके ऊपर है कि (उदाहरण के लिए) एक पदानुक्रम में बहुरंगी वर्गों के साथ काम करने वाला एक फ़ंक्शन सूचक या आधार के माध्यम से ऐसा करता है। कक्षा।

C # में, मूल रूप से प्रकारों के बीच एक ही अलगाव अधिक स्पष्ट है, और भाषा द्वारा ही लागू किया जाता है। प्रोग्रामर को संदर्भ द्वारा किसी वर्ग के उदाहरण को पारित करने के लिए कोई कदम उठाने की आवश्यकता नहीं है, क्योंकि यह डिफ़ॉल्ट रूप से होगा।


2
C ++ प्रशंसक के रूप में, मुझे लगता है कि यह C # और स्विस आर्मी चेनसॉ के बीच के अंतर का एक उत्कृष्ट सारांश है।
डेविड थॉर्नले

1
@DavidThornley: मैंने कम से कम यह लिखने का प्रयास किया कि मुझे क्या लगता है कि यह कुछ हद तक संतुलित तुलना होगी। उंगलियों को इंगित करने के लिए नहीं, लेकिन मैंने जो कुछ देखा था जब मैंने इसे लिखा था तो मुझे इस तरह से मारा गया था ... कुछ हद तक गलत (इसे अच्छी तरह से रखने के लिए)।
जेरी कॉफिन

7

यह "C #: हमें दूसरी भाषा की आवश्यकता क्यों है?" - गुनरसन, एरिक:

सी # के लिए सादगी एक महत्वपूर्ण डिजाइन लक्ष्य था।

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

[...]

मूल्य प्रकार , ऑपरेटर ओवरलोडिंग और उपयोगकर्ता द्वारा परिभाषित रूपांतरण सभी भाषा में जटिलता जोड़ते हैं , लेकिन एक महत्वपूर्ण उपयोगकर्ता परिदृश्य को सरल बनाने की अनुमति देते हैं।

वस्तुओं के लिए संदर्भ शब्दार्थ बहुत सारी परेशानियों से बचने का एक तरीका है (बेशक और केवल ऑब्जेक्ट स्लाइसिंग नहीं) लेकिन वास्तविक दुनिया की समस्याओं को कभी-कभी मूल्य अर्थ के साथ वस्तुओं की आवश्यकता हो सकती है (उदाहरण के लिए ध्वनियों पर एक नज़र डालें जैसे मुझे संदर्भ शब्दार्थ का उपयोग कभी नहीं करना चाहिए, है ना? एक अलग दृष्टिकोण के लिए)।

इसलिए, उन गंदे, बदसूरत और बुरे ऑब्जेक्ट्स को अलग-थलग करने के बजाय, टैग के तहत मूल्य-शब्दार्थ को अलग करने के लिए क्या बेहतर तरीका है struct?


1
मुझे नहीं पता, शायद उन गंदे, बदसूरत, और खराब वस्तुओं का उपयोग नहीं किया जा रहा है-संदर्भ-शब्दार्थ के साथ?
बार्टेक बानचेविच

शायद ... मैं एक खोया हुआ कारण हूं।
manlio

2
IMHO, जावा के डिजाइन में सबसे बड़ा दोष यह घोषित करने के लिए किसी भी साधन की कमी है कि क्या एक चर का उपयोग पहचान या स्वामित्व को घेरने के लिए किया जा रहा है , और C # में सबसे बड़े दोषों में से एक है आपरेशनों को भेद करने के साधन की कमी। किसी ऑब्जेक्ट पर परिचालनों से एक वैरिएबल जिसमें एक वैरिएबल एक संदर्भ रखता है। यहां तक ​​कि अगर रनटाइम ने इस तरह के भेदों की परवाह नहीं की, तो एक भाषा में निर्दिष्ट करने में सक्षम होने के नाते कि क्या एक प्रकार का चर int[]साझा करने योग्य होना चाहिए या परिवर्तनशील (सरणियां या तो हो सकती हैं, लेकिन आम तौर पर दोनों नहीं) गलत कोड को गलत दिखने में मदद करेगा।
सुपरकाट

4

व्युत्पन्न प्रकारों के बारे में सोचने के बजाय Object, यह वर्ग उदाहरण प्रकारों से पूरी तरह से अलग ब्रह्मांड में मौजूद भंडारण-स्थान प्रकारों के बारे में सोचने के लिए अधिक उपयोगी होगा, लेकिन प्रत्येक मूल्य प्रकार के लिए एक समान हीप-ऑब्जेक्ट प्रकार होगा। संरचना प्रकार का एक भंडारण स्थान केवल प्रकार के सार्वजनिक और निजी क्षेत्रों का एक संयोजन रखता है, और ढेर प्रकार एक पैटर्न के अनुसार ऑटो-जनरेट किया जाता है:

// Defined structure
struct Point : IEquatable<Point>
{
  public int X,Y;
  public Point(int x, int y) { X=x; Y=y; }
  public bool Equals(Point other) { return X==other.X && y==other.Y; }
  public bool Equals(Object other)
  { return other != null && other.GetType()==typeof(this) && Equals(Point(other)); }
  public bool ToString() { return String.Format("[{0},{1}", x, y); }
  public bool GetHashCode() { return unchecked(x+y*65531); }
}        
// Auto-generated class
class boxed_Point: IEquatable<Point>
{
  public Point value; // Fake name; C++/CLI, though not C#, allow full access
  public boxed_Point(Point v) { value=v; }
  // Members chain to each member of the original
  public bool Equals(Point other) { return value.Equals(other); }
  public bool Equals(Object other) { return value.Equals(other); }
  public String ToString() { return value.ToString(); }
  public Int32 GetHashCode() { return value.GetHashCode(); }
}

और एक बयान के लिए जैसे: Console.WriteLine ("मान {0}", somePoint) है;

के रूप में अनुवाद करने के लिए: boxed_Point box1 = new boxed_Point (somePoint); Console.WriteLine ("मान {0}" है, box1);

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

कुछ लोग सोचते हैं कि किसी भी प्रकार के मूल्य जो वस्तुओं की तरह व्यवहार नहीं करते हैं उन्हें "बुराई" माना जाना चाहिए। मैं विपरीत दृष्टिकोण लेता हूं: चूंकि मूल्य प्रकारों के भंडारण स्थान न तो ऑब्जेक्ट हैं और न ही ऑब्जेक्ट्स के संदर्भ में, इस अपेक्षा के साथ कि वे वस्तुओं की तरह व्यवहार करना चाहिए, उन्हें अप्रभावी माना जाना चाहिए। ऐसे मामलों में जहां कोई संरचना किसी वस्तु की तरह उपयोगी व्यवहार कर सकती है, ऐसा करने में कुछ भी गलत नहीं है, लेकिन प्रत्येक structइसके दिल में है, जो डक्ट टेप के साथ अटक गए सार्वजनिक और निजी क्षेत्रों के एकत्रीकरण से ज्यादा कुछ नहीं है।

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