उत्परिवर्ती संरचनाएं "बुराई" क्यों हैं?


485

एसओ पर यहां चर्चा के बाद मैंने पहले ही कई बार टिप्पणी पढ़ी कि परिवर्तनशील संरचनाएं "बुराई" हैं (जैसे इस प्रश्न के उत्तर में )।

C # में परिवर्तनशीलता और संरचना के साथ वास्तविक समस्या क्या है?


12
उत्परिवर्तनीय संरचनाओं का दावा करना बुराई है, उत्परिवर्तनीय intएस, boolएस का दावा करने जैसा है , और अन्य सभी मूल्य प्रकार बुराई हैं। उत्परिवर्तन और अपरिवर्तनीयता के मामले हैं। उन मामलों में डेटा की भूमिका पर टिका हुआ है, स्मृति आवंटन / साझाकरण का प्रकार नहीं।
स्लिप डी। थॉम्पसन

41
@slipp intऔर परस्पर नहींbool हैं ..
Blorgbeard

2
.-साइनटैक्स, रिफ-टाइप किए गए डेटा और वैल्यू-टाइप किए गए डेटा के साथ ऑपरेशन करने पर भी समान रूप से अलग-अलग दिखते हैं। यह C # के गुणों का दोष है, न कि संरचनों का- कुछ भाषाएं a[V][X] = 3.14इन- प्लेसिंग को बदलने के लिए एक वैकल्पिक वाक्यविन्यास प्रदान करती हैं । C # में, आप 'MutateV (एक्शन <Ref वेक्टर2> म्यूटेटर)' जैसे स्ट्रक्चर-मेंबर म्यूटेटर के तरीकों की पेशकश करने के लिए बेहतर करेंगे और इसका उपयोग करें a.MutateV((v) => { v.X = 3; }) (उदाहरण के लिए refकीवर्ड के संबंध में C # की सीमाओं के कारण ओवर-सरलीकृत किया गया है , लेकिन कुछ के साथ workarounds संभव होना चाहिए)
स्लिप डी। थॉम्पसन 19

2
@ स्लिप खैर, मुझे लगता है कि इस तरह की संरचना के बारे में बिल्कुल विपरीत है। आपको क्यों लगता है कि पहले से ही .NET लाइब्रेरी में लागू होने वाली संरचनाएँ, जैसे डेटटाइम या टाइमस्पैन (इसी तरह की) अपरिवर्तनीय हैं? हो सकता है कि इस तरह की संरचना के संस्करण के केवल एक सदस्य को बदलना उपयोगी हो, लेकिन यह सिर्फ बहुत असुविधाजनक है, बहुत अधिक समस्याओं की ओर जाता है। वास्तव में आप गलत हैं कि प्रोसेसर क्या करता है, चूंकि C # कोडांतरक के लिए संकलित नहीं करता है, यह IL को संकलित करता है। IL में (बशर्ते हमारे पास पहले से ही नामांकित चर है x) यह एकल ऑपरेशन 4 निर्देश हैं: ldloc.0(0-सूचकांक चर को इसमें लोड करता है ...
सुशी 271

1
... प्रकार। Tप्रकार है Ref सिर्फ एक कीवर्ड है जो वैरिएबल को एक विधि में ही पास करता है, इसकी एक प्रति नहीं। संदर्भ प्रकारों के लिए भी इसका अर्थ है, क्योंकि हम चर को बदल सकते हैं , अर्थात विधि के बाहर का संदर्भ विधि के भीतर बदले जाने के बाद अन्य ऑब्जेक्ट को इंगित करेगा। चूंकि ref Tएक प्रकार नहीं है, लेकिन एक विधि पैरामीटर को पारित करने का फैशन है, आप इसे नहीं डाल सकते हैं <>, क्योंकि केवल प्रकार को वहां रखा जा सकता है। तो यह सिर्फ गलत है। शायद ऐसा करना सुविधाजनक होगा, हो सकता है कि सी # टीम कुछ नए संस्करण के लिए इसे बना सके, लेकिन अभी वे कुछ पर काम कर रहे हैं ...
सुशी 271

जवाबों:


290

संरचनाएं मूल्य प्रकार हैं जिसका अर्थ है कि जब वे चारों ओर से गुजरते हैं तो उनकी नकल की जाती है।

इसलिए यदि आप एक प्रति बदलते हैं तो आप केवल उस प्रति को बदल रहे हैं, न कि मूल और न ही किसी अन्य प्रति को जो आसपास हो सकती है।

यदि आपकी संरचना अपरिवर्तनीय है, तो मूल्य से पारित होने के परिणामस्वरूप होने वाली सभी स्वचालित प्रतियां समान होंगी।

यदि आप इसे बदलना चाहते हैं, तो आपको सचेत रूप से संशोधित डेटा के साथ संरचना का एक नया उदाहरण बनाकर करना होगा। (प्रति नहीं)


82
"यदि आपकी संरचना अपरिवर्तनीय है तो सभी प्रतियाँ समान होंगी।" नहीं, इसका मतलब है कि अगर आपको अलग मूल्य चाहिए तो आपको सचेत रूप से एक प्रति बनानी होगी। इसका मतलब है कि आप एक कॉपी को संशोधित नहीं करेंगे, यह सोचकर कि आप मूल को संशोधित कर रहे हैं।
लुकास

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

3
आपका संपादन (16 महीने बाद) वह थोड़ा साफ करता है। मैं अभी भी "(अपरिवर्तनीय संरचना) से खड़ा हूं इसका मतलब है कि आप एक कॉपी को संशोधित करते हुए नहीं सोचेंगे कि आप मूल को संशोधित कर रहे हैं", हालांकि।
लुकास

6
@ लुकास: किसी संरचना की प्रतिलिपि बनाने का खतरा, इसे संशोधित करना, और किसी भी तरह से किसी एक को मूल रूप में संशोधित करना (जब तथ्य यह है कि कोई एक संरचनात्मक क्षेत्र लिख रहा है, इस तथ्य को स्पष्ट करता है कि व्यक्ति केवल एक की प्रतिलिपि लिख रहा है) इस खतरे की तुलना में बहुत छोटा है कि कोई व्यक्ति जो अपने पास मौजूद जानकारी को रखने के साधन के रूप में एक क्लास ऑब्जेक्ट रखता है, वह वस्तु को अपनी जानकारी अपडेट करने के लिए म्यूट करेगा और इस प्रक्रिया में किसी अन्य ऑब्जेक्ट द्वारा रखी गई जानकारी को दूषित कर देगा।
सुपरकैट

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

167

कहां से शुरू करें?;

एरिक लिपर्ट का ब्लॉग हमेशा एक उद्धरण के लिए अच्छा होता है:

यह अभी तक एक और कारण है कि क्यों परिवर्तनशील मूल्य प्रकार बुराई हैं। हमेशा मूल्य प्रकार अपरिवर्तनीय बनाने का प्रयास करें।

सबसे पहले, आप बहुत आसानी से परिवर्तन खो देते हैं ... उदाहरण के लिए, किसी सूची से चीजें प्राप्त करना:

Foo foo = list[0];
foo.Name = "abc";

वह क्या बदल गया? कुछ भी उपयोगी नहीं है ...

गुणों के साथ समान:

myObj.SomeProperty.Size = 22; // the compiler spots this one

आपको करने के लिए मजबूर:

Bar bar = myObj.SomeProperty;
bar.Size = 22;
myObj.SomeProperty = bar;

कम गंभीर रूप से, एक आकार का मुद्दा है; परिवर्तनशील वस्तुओं करते हैं कई गुण होते हैं करने के लिए; फिर भी यदि आपके पास दो intएस, ए string, ए DateTimeऔर ए के साथ एक संरचना है bool, तो आप बहुत जल्दी स्मृति के माध्यम से जला सकते हैं। एक वर्ग के साथ, कई कॉल करने वाले एक ही उदाहरण के संदर्भ साझा कर सकते हैं (संदर्भ छोटे हैं)।


5
खैर हाँ लेकिन संकलक इस तरह से बेवकूफ है। प्रॉपर्टी-स्ट्रक्चर मेंबर्स को असाइनमेंट की अनुमति नहीं देना आईएमएचओ को बेवकूफी भरा फैसला था, क्योंकि यह है के लिए अनुमति दी ++ऑपरेटर। इस मामले में, कंपाइलर केवल प्रोग्रामर को हस्ट करने के बजाय स्पष्ट असाइनमेंट लिखता है।
कोनराड रुडोल्फ

12
@ कोनराड: myObj.SomeProperty.Size = 22 myObj.SomeProperty की एक प्रति संशोधित करेगा। संकलक आपको एक स्पष्ट बग से बचा रहा है। और यह ++ के लिए अनुमति नहीं है।
लुकास

@ लुकास: खैर, मोनो सी # कंपाइलर निश्चित रूप से इसकी अनुमति देता है - चूंकि मेरे पास विंडोज नहीं है इसलिए मैं माइक्रोसॉफ्ट के कंपाइलर की जांच नहीं कर सकता।
कोनराड रुडोल्फ

6
@ कोनराड - एक कम अप्रत्यक्ष के साथ यह काम करना चाहिए; यह "किसी वस्तु का ऐसा मूल्य है जो केवल स्टैक पर एक क्षणिक मूल्य के रूप में मौजूद है और जो शून्य में वाष्पित होने वाला है" जो कि अवरुद्ध है।
मार्क ग्रेवेल

2
@Marc Gravell: कोड के पूर्व टुकड़े में, आप एक "फू" जिसका नाम "एबीसी" है और जिसकी अन्य विशेषताएँ सूची [0] के हैं, बिना सूची में गड़बड़ी के समाप्त हो जाती हैं [0]। यदि फू एक वर्ग थे, तो इसे क्लोन करना और फिर कॉपी बदलना आवश्यक होगा। मेरे दिमाग में, मूल्य-प्रकार बनाम वर्ग भेद के साथ बड़ी समस्या "का उपयोग है।" दो उद्देश्यों के लिए ऑपरेटर। यदि मेरे पास मेरे शराबी होते, तो कक्षाएं दोनों का समर्थन कर सकती थीं "।" और "->" विधियों और गुणों के लिए, लेकिन "" के लिए सामान्य शब्दार्थ। संपत्तियों को संशोधित करने के लिए उपयुक्त क्षेत्र के साथ एक नया उदाहरण बनाना होगा।
सुपरकैट

71

मैं बुराई नहीं कहूंगा, लेकिन अधिकतम कार्यक्षमता प्रदान करने के लिए प्रोग्रामर की ओर से उत्परिवर्तन अक्सर अतिशयोक्ति का संकेत है। वास्तव में, इसकी अक्सर आवश्यकता नहीं होती है और यह बदले में, इंटरफ़ेस को छोटा, प्रयोग करने में आसान और गलत (अधिक मजबूत) का उपयोग करने के लिए आसान बनाता है।

इसका एक उदाहरण है, पढ़ना / लिखना और लिखना / दौड़ की स्थितियों में संघर्ष लिखना। ये केवल अपरिवर्तनीय संरचनाओं में नहीं हो सकते हैं, क्योंकि एक लेखन एक वैध ऑपरेशन नहीं है।

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


1
एरिक लिपर्ट कहते हैं कि वे हैं ... मेरा जवाब देखें।
मार्क Gravell

42
जितना मैं एरिक लिपर्ट का सम्मान करता हूं वह भगवान नहीं है (या कम से कम अभी तक नहीं)। जिस ब्लॉग पर आप लिंक करते हैं और ऊपर आपकी पोस्ट होती है, वह निश्चित रूप से संरचना को अपरिवर्तनीय बनाने के लिए उचित तर्क हैं, लेकिन वे वास्तव में बहुत कमजोर हैं, जो कि उत्परिवर्ती संरचनाओं का उपयोग नहीं करने के लिए तर्क हैं । हालाँकि, यह पोस्ट +1 है।
स्टीफन मार्टिन

2
C # में विकसित करना, आपको आमतौर पर अब और फिर - कभी-कभी अपने बिजनेस मॉडल के साथ पारस्परिकता की आवश्यकता होती है, जहां आप मौजूदा समाधानों को सुचारू रूप से काम करने के लिए स्ट्रीमिंग आदि चाहते हैं। मैंने उत्परिवर्तनीय और अपरिवर्तनीय डेटा के साथ काम करने के बारे में एक लेख लिखा था, जो उत्परिवर्तन के आसपास के अधिकांश मुद्दों को हल करता है (मुझे उम्मीद है): rickyhelgesson.wordpress.com/2012/07/17/…
रिकी हेल्गेसन

3
: समूह। उस उद्देश्य के लिए उपयोग की जाने वाली संरचनाएं आमतौर पर सार्वजनिक क्षेत्रों के रूप में अपने चर को उजागर करना चाहिए। मैं इस धारणा पर विचार करूंगा कि इस तरह के उद्देश्यों के लिए एक संरचना की तुलना में एक वर्ग का उपयोग करना अधिक उचित है, सिर्फ सादा गलत। अपरिवर्तनीय कक्षाएं अक्सर कम कुशल होती हैं, और उत्परिवर्तित कक्षाओं में अक्सर भयानक शब्दार्थ होते हैं।
सुपरकैट

2
@StephenMartin: उदाहरण के लिए, एक विधि या संपत्ति जो floatएक ग्राफिक्स रूपांतरण के छह घटकों को वापस करने वाली है, पर विचार करें । यदि ऐसी विधि छह घटकों के साथ एक उजागर-क्षेत्र संरचना लौटाती है, तो यह स्पष्ट है कि संरचना के क्षेत्र को संशोधित करने से ग्राफिक्स ऑब्जेक्ट को संशोधित नहीं किया जाएगा, जहां से इसे प्राप्त किया गया था। यदि इस तरह की विधि एक उत्परिवर्तनीय श्रेणी की वस्तु लौटाती है, तो शायद इसके गुणों को बदलने से अंतर्निहित ग्राफिक्स ऑब्जेक्ट बदल जाएगा और शायद यह नहीं होगा - कोई भी वास्तव में नहीं जानता है।
23

48

उत्परिवर्ती संरचनाएं बुराई नहीं हैं।

उच्च प्रदर्शन परिस्थितियों में वे बिल्कुल आवश्यक हैं। उदाहरण के लिए जब कैश लाइनें और कचरा संग्रह एक अड़चन बन जाता है।

मैं इन पूरी तरह से मान्य उपयोग-मामलों में "बुराई" में एक अपरिवर्तनीय संरचना के उपयोग को नहीं कहूंगा।

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

हालांकि, अपरिवर्तनीय संरचनाओं को "बुराई" के रूप में लेबल करने के बजाय, मैं भाषा को गले लगाने और अंगूठे के अधिक सहायक और रचनात्मक नियम की वकालत करने की सलाह दूंगा।

उदाहरण के लिए: "संरचनाएं मूल्य प्रकार हैं, जिन्हें डिफ़ॉल्ट रूप से कॉपी किया जाता है। आपको एक संदर्भ की आवश्यकता है यदि आप उन्हें कॉपी नहीं करना चाहते हैं" या "पहले पठनीय संरचनाओं के साथ काम करने का प्रयास करें"


8
मैं यह भी बताऊंगा कि यदि कोई डक्ट टेप के साथ चर का एक निश्चित सेट को जकड़ना चाहता है, तो उनके मूल्यों को संसाधित किया जा सकता है या अलग से या एक इकाई के रूप में संग्रहीत किया जा सकता है, यह कंपाइलर को एक निश्चित सेट को फास्ट करने के लिए कहने के लिए बहुत अधिक समझ में आता है। एक साथ चर (यानी structसार्वजनिक क्षेत्रों के साथ घोषित करें ) एक वर्ग को परिभाषित करने की तुलना में, जिसका उपयोग किया जा सकता है, अनाड़ी रूप से, एक ही छोर को प्राप्त करने के लिए, या इसे ऐसे वर्ग का अनुकरण करने के लिए एक संरचना में कबाड़ का एक गुच्छा जोड़ने के लिए (बजाय होने के बजाय) डक्ट टेप के साथ एक साथ अटके हुए चर के एक सेट की तरह व्यवहार करें, जो कि वास्तव में पहली जगह में क्या चाहता है)
Supercat

41

सार्वजनिक उत्परिवर्तित क्षेत्रों या गुणों वाली संरचनाएं बुराई नहीं हैं।

संरचना विधियां (संपत्ति बसने वालों से अलग) जो "इस" को म्यूट करती हैं, केवल इसलिए कि यह .net उन्हें उन तरीकों से अलग करने का साधन प्रदान नहीं करता है जो नहीं करते हैं। संरचना विधियाँ जो "इस" को उत्परिवर्तित नहीं करती हैं, उन्हें केवल रक्षात्मक प्रतिलिपि की आवश्यकता के बिना केवल-पढ़ी गई संरचना पर भी अदृश्य होना चाहिए। जो विधियाँ "यह" करती हैं, उन्हें केवल-पढ़ने के लिए संरचितों पर लागू नहीं किया जाना चाहिए। चूँकि .net उन संरचनात्मक विधियों को मना नहीं करना चाहता जो "इसे" को केवल-पढ़ी हुई संरचना पर लागू होने से संशोधित नहीं करती हैं, लेकिन केवल-पढ़ी हुई संरचना को उत्परिवर्तित करने की अनुमति नहीं देना चाहती हैं, यह रक्षात्मक रूप से पढ़ी गई रचनाओं की प्रतिलिपि बनाती है। केवल संदर्भ, यकीनन दोनों दुनिया के सबसे खराब हो रहे हैं।

केवल-पढ़ने के संदर्भों में स्व-उत्परिवर्तन विधियों की हैंडलिंग के साथ समस्याओं के बावजूद, उत्परिवर्तनीय संरचनाएं अक्सर शब्दार्थों को उत्परिवर्ती वर्ग प्रकारों से बेहतर प्रदान करती हैं। निम्नलिखित तीन विधि हस्ताक्षर पर विचार करें:

संरचना PointyStruct {सार्वजनिक int x, y, z;};
वर्ग PointyClass {सार्वजनिक int x, y, z;};

शून्य विधि 1 (पॉइन्टीस्ट्रक्ट फू);
शून्य मेथड 2 (रिफ पॉइन्टीस्ट्रक्ट फू);
void Method3 (PointyClass foo);

प्रत्येक विधि के लिए, निम्नलिखित प्रश्नों के उत्तर दें:

  1. यह मानते हुए कि विधि किसी भी "असुरक्षित" कोड का उपयोग नहीं करती है, क्या यह फू को संशोधित कर सकता है?
  2. यदि विधि कहे जाने से पहले 'फू' का कोई बाहरी संदर्भ मौजूद नहीं है, तो क्या बाहर का संदर्भ मौजूद हो सकता है?

उत्तर:

प्रश्न 1::
Method1()नहीं (स्पष्ट इरादा)
Method2() : हाँ (स्पष्ट इरादा)
Method3() : हाँ (अनिश्चित इरादे)
प्रश्न 2
Method1():: नहीं
Method2(): नहीं (जब तक असुरक्षित)
Method3() : हाँ

Method1 फू को संशोधित नहीं कर सकता है, और कभी भी संदर्भ नहीं मिलता है। Method2 को फू के लिए एक अल्पकालिक संदर्भ मिलता है, जिसका उपयोग यह किसी भी क्रम में, किसी भी क्रम में, किसी भी समय foo के फ़ील्ड को संशोधित करने के लिए कर सकता है, लेकिन यह उस संदर्भ को जारी नहीं रख सकता। Method2 रिटर्न से पहले, जब तक कि यह असुरक्षित कोड का उपयोग नहीं करता है, तब तक इसके 'फू' संदर्भ से बनी कोई भी और सभी प्रतियां गायब हो गई होंगी। Method3, Method2 के विपरीत, फू को प्रोमिस्युली-शार्बल रेफरेंस मिलता है, और इसके साथ क्या हो सकता है, यह कोई नहीं बता रहा है। यह फू को बिल्कुल नहीं बदल सकता है, यह फू को बदल सकता है और फिर वापस आ सकता है, या हो सकता है कि यह किसी अन्य धागे को फू का संदर्भ दे सकता है जो भविष्य के कुछ मनमाने तरीके से इसे मनमाने तरीके से बदल सकता है।

संरचनाओं की सरणियाँ अद्भुत शब्दार्थ प्रदान करती हैं। RectArray [500] प्रकार के आयत को देखते हुए, यह स्पष्ट और स्पष्ट है कि प्रतिलिपि तत्व 123 से तत्व 456 तक और फिर कुछ समय बाद तत्व 123 की चौड़ाई को निर्धारित किए बिना, तत्व को 456 पर सेट किए बिना। "RectArray [432] = RectArray [321 ]; ...; RectArray [123] .Width = 555; "। यह जानकर कि आयत एक पूर्णांक क्षेत्र के साथ एक संरचना है जिसे चौड़ाई कहा जाता है, जो उपरोक्त सभी कथनों के बारे में जानने के लिए सभी को बताएगा।

अब मान लें कि रेक्टक्लास आयतों के समान ही एक वर्ग था और एक रेक्टक्लास के RectClassArray [500] पर समान संचालन करना चाहता था। संभवतः सरणी को RableClass की उत्परिवर्तित वस्तुओं के लिए 500 पूर्व-प्रारंभिक अपरिवर्तनीय संदर्भों को धारण करना चाहिए। उस स्थिति में, उचित कोड कुछ "RectClassArray [321] .SetBounds (RectClassArray [456]) जैसा होगा; ...; RectClassArray [321] .X = 555;"। शायद सरणी को ऐसे उदाहरणों को धारण करने के लिए माना जाता है जो बदलने नहीं जा रहे हैं, इसलिए उचित कोड "RectClassArray [321] = RectClassArray [456] ...; RectClassArray [321] = New RectClass (RectClassArray [321) की तरह होगा; ]); रेक्टक्लासअरे [321] .X = 555; " यह जानने के लिए कि किसी को क्या करना है, दोनों को RectClass के बारे में बहुत कुछ जानना होगा (जैसे कि यह एक कॉपी कंस्ट्रक्टर, एक कॉपी-मेथड विधि आदि का समर्थन करता है)। ) और सरणी का इच्छित उपयोग। एक संरचना का उपयोग करने के रूप में कहीं भी साफ नहीं है।

यह सुनिश्चित करने के लिए कि किसी सरणी के अलावा किसी कंटेनर श्रेणी के लिए दुर्भाग्य से कोई अच्छा तरीका नहीं है, जो किसी संरचना सरणी के स्वच्छ शब्दार्थ को प्रस्तुत करता है। सबसे अच्छा एक कर सकता है, अगर कोई एक संग्रह को उदाहरण के लिए एक स्ट्रिंग के साथ अनुक्रमित करना चाहता था, तो संभवतः एक सामान्य "ActOnItem" विधि की पेशकश करेगा, जो सूचकांक के लिए एक स्ट्रिंग को स्वीकार करेगा, एक सामान्य पैरामीटर, और एक प्रतिनिधि जो पारित होगा। जेनेरिक पैरामीटर और संग्रह आइटम दोनों के संदर्भ में। यह लगभग समान शब्दार्थों को संरचनात्मक सरणियों के रूप में अनुमति देता है, लेकिन जब तक कि vb.net और C # लोगों को एक अच्छा वाक्यविन्यास पेश करने के लिए पीछा नहीं किया जा सकता है, तब तक कोड क्लिंक-दिखने वाला होता है, भले ही यह यथोचित प्रदर्शन हो (एक सामान्य पैरामीटर पास करना) एक स्थिर प्रतिनिधि के उपयोग के लिए अनुमति दें और किसी भी अस्थायी वर्ग उदाहरण बनाने की आवश्यकता से बचें)।

व्यक्तिगत रूप से, मुझे घृणा करने वाले एरिक लिपर्ट एट अल पर गर्व है। उत्परिवर्तनीय मूल्य प्रकारों के बारे में बताया। वे सभी स्थान पर उपयोग किए जाने वाले उचित संदर्भ प्रकारों की तुलना में बहुत अधिक स्वच्छ शब्दार्थ प्रदान करते हैं। मूल्य प्रकारों के लिए .net के समर्थन के साथ कुछ सीमाओं के बावजूद, ऐसे कई मामले हैं जहां परस्पर मूल्य प्रकार किसी भी अन्य प्रकार की इकाई की तुलना में बेहतर फिट हैं।


1
@ रॉन वारहोलिक: यह स्व-स्पष्ट नहीं है कि SomeRect एक आयत है। यह कुछ अन्य प्रकार का हो सकता है जो कि आयत से स्पष्ट रूप से टाइपकास्ट हो सकता है। हालांकि, एकमात्र सिस्टम-परिभाषित प्रकार जो कि आयत से आयत टाइपकास्ट हो सकता है, रेक्टैंगलएफ है, और कंपाइलर स्क्वॉक करेगा यदि कोई रेक्टैंगलएफ के क्षेत्रों को आयत के निर्माता के पास देने की कोशिश करता है (क्योंकि पूर्व एकल हैं, और बाद के पूर्णांक) , उपयोगकर्ता-परिभाषित संरचनाएं हो सकती हैं जो इस तरह के निहित टाइपकास्ट की अनुमति देती हैं। BTW, पहला कथन समान रूप से अच्छी तरह से काम करेगा कि क्या SomeRect एक आयत या RectangleF था।
सुपरकैट

आपके द्वारा दिखाया गया है कि एक आकस्मिक उदाहरण में आप मानते हैं कि एक विधि स्पष्ट है। अगर हम आपका उदाहरण लेते हैं तो Rectangleमैं आसानी से एक सामान्य स्थिति में आ सकता हूँ जहाँ आपको अत्यधिक अस्पष्ट व्यवहार मिलता है। इस बात पर विचार करें कि WinForms एक उत्परिवर्तनीय Rectangleप्रकार को Boundsगुण के रूप में उपयोग करता है । अगर मैं सीमाएँ बदलना चाहता हूँ, तो मैं आपके अच्छे वाक्यविन्यास का उपयोग करना चाहता हूँ: form.Bounds.X = 10;हालाँकि यह रूप पर कुछ भी नहीं बदलता है (और आपको इस तरह की सूचना देने वाली एक सुंदर त्रुटि उत्पन्न करता है)। असंगति प्रोग्रामिंग का प्रतिबंध है और यही कारण है कि अपरिवर्तनीयता चाहता है।
रॉन वारहोलिक

3
Warholic @Ron: Btw, मैं होगा की तरह कहने के लिए सक्षम होने के लिए "form.Bounds.X = 10;" और यह सिर्फ काम है, लेकिन सिस्टम ऐसा करने का कोई भी साफ तरीका प्रदान नहीं करता है। कॉल-बैक को स्वीकार करने वाले तरीकों के रूप में मूल्य-प्रकार के गुणों को उजागर करने के लिए एक सम्मेलन वर्गों का उपयोग करने वाले किसी भी दृष्टिकोण की तुलना में बहुत क्लीनर, कुशल और पुष्टि-सही कोड पेश कर सकता है।
सुपरकैट

4
यह जवाब कुछ शीर्ष मतदान के जवाबों की तुलना में बहुत अधिक व्यावहारिक है। यह बेतुका है कि उत्परिवर्तनीय मूल्य प्रकारों के खिलाफ तर्क अलियासिंग और उत्परिवर्तन को मिलाते समय "क्या आप उम्मीद करते हैं" की धारणा पर निर्भर करता है। यह किसी भी तरह से एक भयानक बात है !
ईमोन नेरबोन

2
@ सुपरकैट: कौन जानता है, हो सकता है कि सी -7 के लिए वे जिस रेफरी-रिटर्न सुविधा के बारे में बात कर रहे हों, वह उस आधार को कवर करे (मैंने वास्तव में इसे विस्तार से नहीं देखा है, लेकिन यह सतही रूप से समान लगता है)।
Eamon Nerbonne

24

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

जैसा कि कोनराड का उल्लेख है कि यह किसी तारीख को बदलने का कोई मतलब नहीं है, क्योंकि मूल्य समय में उस अद्वितीय बिंदु का प्रतिनिधित्व करता है और एक समय वस्तु का उदाहरण नहीं है, जिसमें कोई भी राज्य या संदर्भ-निर्भरता है।

आशा है कि यह आपके लिए कोई मायने रखता है। यह उस अवधारणा के बारे में अधिक है जिसे आप व्यावहारिक विवरणों की तुलना में मूल्य प्रकारों के साथ पकड़ने की कोशिश करते हैं, सुनिश्चित करें।


खैर, वे चाहिए कम से कम ;-p पर अडिग अवधारणाओं, प्रतिनिधित्व करते हैं
मार्क Gravell

3
खैर, मुझे लगता है कि वे System.Drawing.Point को अपरिवर्तनीय बना सकते थे लेकिन यह एक गंभीर डिजाइन त्रुटि IMHO रही होगी। मुझे लगता है कि अंक वास्तव में एक कट्टरपंथी मूल्य प्रकार हैं और वे परस्पर भिन्न हैं। और वे वास्तव में शुरुआती प्रोग्रामिंग 101 शुरुआती से परे किसी के लिए कोई समस्या पैदा नहीं करते हैं।
स्टीफन मार्टिन

3
सिद्धांत रूप में, मुझे लगता है कि अंक भी अपरिवर्तनीय होना चाहिए, लेकिन अगर यह निश्चित रूप से उपयोग करने के लिए कठिन या कम सुरुचिपूर्ण बनाता है, तो उस पर भी विचार करना होगा। कोड निर्माण का कोई मतलब नहीं है, जो बेहतरीन राजकुमारों को बनाए रखता है यदि कोई भी उनका उपयोग नहीं करना चाहता है;)
मोर्टेन क्रिस्चियन

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

4
"मूल्य प्रकार मूल रूप से अपरिवर्तनीय अवधारणाओं का प्रतिनिधित्व करते हैं"। नहीं, वे नहीं। मूल्य-टाइप किए गए चर के सबसे पुराने और सबसे उपयोगी अनुप्रयोगों में से एक intइट्रेटर है, जो अपरिवर्तनीय होने पर पूरी तरह से बेकार हो जाएगा। मुझे लगता है कि आप "वैल्यू टाइप 'कंपाइलर / रनटाइम इम्प्लीमेंटेशन" वैल्यू टाइप में टाइप किए गए वेरिएबल "को कन्फ्यूज कर रहे हैं - बाद वाला निश्चित रूप से किसी भी संभावित वैल्यू से भिन्न है।
स्लिप डी। थॉम्पसन

19

कुछ अन्य कोने के मामले हैं जो प्रोग्रामर के दृष्टिकोण से अप्रत्याशित व्यवहार का कारण बन सकते हैं।

अपरिवर्तनीय मूल्य प्रकार और आसानी से फ़ील्ड

    // Simple mutable structure. 
    // Method IncrementI mutates current state.
    struct Mutable
    {
        public Mutable(int i) : this() 
        {
            I = i;
        }

        public void IncrementI() { I++; }

        public int I { get; private set; }
    }

    // Simple class that contains Mutable structure
    // as readonly field
    class SomeClass 
    {
        public readonly Mutable mutable = new Mutable(5);
    }

    // Simple class that contains Mutable structure
    // as ordinary (non-readonly) field
    class AnotherClass 
    {
        public Mutable mutable = new Mutable(5);
    }

    class Program
    {
        void Main()
        {
            // Case 1. Mutable readonly field
            var someClass = new SomeClass();
            someClass.mutable.IncrementI();
            // still 5, not 6, because SomeClass.mutable field is readonly
            // and compiler creates temporary copy every time when you trying to
            // access this field
            Console.WriteLine(someClass.mutable.I);

            // Case 2. Mutable ordinary field
            var anotherClass = new AnotherClass();
            anotherClass.mutable.IncrementI();

            // Prints 6, because AnotherClass.mutable field is not readonly
            Console.WriteLine(anotherClass.mutable.I);
        }
    }

म्यूटेबल मूल्य प्रकार और सरणी

मान लीजिए हमारे पास हमारी Mutableसंरचना की एक सरणी है और हम IncrementIउस सरणी के पहले तत्व के लिए विधि को बुला रहे हैं । इस कॉल से आप किस व्यवहार की उम्मीद कर रहे हैं? क्या यह एरे के मूल्य या केवल एक प्रति को बदलना चाहिए?

    Mutable[] arrayOfMutables = new Mutable[1];
    arrayOfMutables[0] = new Mutable(5);

    // Now we actually accessing reference to the first element
    // without making any additional copy
    arrayOfMutables[0].IncrementI();

    // Prints 6!!
    Console.WriteLine(arrayOfMutables[0].I);

    // Every array implements IList<T> interface
    IList<Mutable> listOfMutables = arrayOfMutables;

    // But accessing values through this interface lead
    // to different behavior: IList indexer returns a copy
    // instead of an managed reference
    listOfMutables[0].IncrementI(); // Should change I to 7

    // Nope! we still have 6, because previous line of code
    // mutate a copy instead of a list value
    Console.WriteLine(listOfMutables[0].I);

इसलिए, जब तक आप और बाकी टीम स्पष्ट रूप से समझ नहीं पाती है कि आप क्या कर रहे हैं। लेकिन बहुत सारे कोने के मामले हैं जब कार्यक्रम का व्यवहार उम्मीद से अलग होगा, जिससे उत्पादन में कठिनता हो सकती है और त्रुटियों को समझना मुश्किल हो सकता है।


5
क्या होना चाहिए, अगर .net भाषाओं में थोड़ा बेहतर मूल्य-प्रकार का समर्थन होता है, तो संरचनात्मक विधियों को 'यह' करने से मना किया जाना चाहिए जब तक कि उन्हें स्पष्ट रूप से ऐसा करने की घोषणा नहीं की जाती है, और जो विधियाँ घोषित की जाती हैं उन्हें केवल पढ़ने के लिए मना किया जाना चाहिए संदर्भों। उत्परिवर्तनीय संरचनाओं की सारणियाँ उपयोगी शब्दार्थ प्रदान करती हैं जिन्हें अन्य माध्यमों से कुशलता से प्राप्त नहीं किया जा सकता है।
सुपरकट

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

@Sahuagin: दुर्भाग्य से, कोई मानक तंत्र नहीं है जिसके द्वारा एक इंटरफ़ेस एक संदर्भ को उजागर कर सकता है। ऐसे तरीके हैं। नेट ऐसी चीजों को सुरक्षित और उपयोगी तरीके से करने की अनुमति दे सकता है (जैसे कि एक विशेष "ArrayRef <T>" संरचना जिसमें एक T[]पूर्णांक सूचकांक होता है, और यह प्रदान करता है कि प्रकार की संपत्ति तक पहुंच की ArrayRef<T>व्याख्या एक के रूप में की जाएगी। उपयुक्त सरणी तत्व तक पहुंच)] [यदि कोई वर्ग ArrayRef<T>किसी अन्य उद्देश्य के लिए उजागर करना चाहता था , तो वह एक विधि प्रदान कर सकता है - जैसा कि एक संपत्ति के विपरीत - इसे पुनः प्राप्त करने के लिए]। दुर्भाग्य से, ऐसे कोई प्रावधान मौजूद नहीं हैं।
सुपरकैट

2
ओह माई ... यह म्यूटेबल स्ट्रक्चर्स को बहुत बुरा बनाता है!
नवाफल

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

18

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

तो, उत्परिवर्ती संरचनाएं बुराई नहीं हैं, C # ने उन्हें बुराई बना दिया है। मैं हर समय C ++ में उत्परिवर्ती संरचनाओं का उपयोग करता हूं और वे बहुत सुविधाजनक और सहज हैं। इसके विपरीत, C # ने मुझे वस्तुओं को संभालने के तरीके के कारण पूरी तरह से कक्षाओं के सदस्यों के रूप में संरचनाओं को छोड़ दिया है। उनकी सुविधा ने हमें हमारी लागत दी है।


संरचना प्रकारों के वर्ग क्षेत्र होने से अक्सर एक बहुत ही उपयोगी पैटर्न हो सकता है, हालांकि माना जाता है कि कुछ सीमाएं हैं। यदि फ़ील्ड के बजाय गुणों का उपयोग करता है या उपयोग करता है readonly, तो प्रदर्शन को नीचा दिखाया जाएगा , लेकिन यदि कोई उन चीज़ों को करने से बचता है तो संरचना के प्रकार के फ़ील्ड फ़ील्ड ठीक हैं। संरचनाओं की एकमात्र मौलिक सीमा यह है कि एक उत्परिवर्तित श्रेणी के प्रकार का एक संरचनात्मक क्षेत्र int[]पहचान को अतिक्रमण कर सकता है या मूल्यों का अपरिवर्तनीय सेट कर सकता है, लेकिन इसका उपयोग एक अपरिवर्तनीय पहचान को अतिक्रमण किए बिना उत्परिवर्तित मूल्यों को एनकैप करने के लिए भी किया जा सकता है।
सुपरकैट

13

यदि आप उन संरचनाओं से चिपके रहते हैं जो (C # में, विजुअल बेसिक 6, पास्कल / डेल्फी, C ++ स्ट्रक्चर टाइप (या क्लासेस) के लिए प्रयुक्त होती हैं, जब उन्हें पॉइंटर्स के रूप में उपयोग नहीं किया जाता है), तो आप पाएंगे कि एक स्ट्रक्चर कंपाउंड वैरिएबल से अधिक नहीं है । इसका मतलब है: आप उन्हें एक सामान्य नाम (आप से संदर्भ सदस्यों को रिकॉर्ड करने वाला चर) के तहत चर के एक भरे हुए सेट के रूप में मानेंगे।

मुझे पता है कि बहुत से लोग OOP को गहराई से इस्तेमाल करने के लिए भ्रमित करेंगे, लेकिन यह कहने के लिए पर्याप्त कारण नहीं है कि ऐसी चीजें स्वाभाविक रूप से बुराई हैं, अगर इसे सही तरीके से उपयोग किया जाए। कुछ संरचनाएं अयोग्य हैं क्योंकि वे इरादा रखते हैं (यह पायथन का मामला है namedtuple), लेकिन यह विचार करने के लिए एक और प्रतिमान है।

हां: संरचना में बहुत सारी मेमोरी शामिल होती है, लेकिन ऐसा करने से अधिक मेमोरी ठीक नहीं होगी:

point.x = point.x + 1

की तुलना में:

point = Point(point.x + 1, point.y)

स्मृति की खपत कम से कम एक ही होगी, या अयोग्य मामले में और भी अधिक (हालांकि यह मामला अस्थायी होगा, वर्तमान स्टैक के लिए, भाषा के आधार पर)।

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

एक अच्छा उदाहरण चाहते हैं? फ़ाइलों को आसानी से पढ़ने के लिए संरचनाओं का उपयोग किया जाता है। पायथन के पास यह पुस्तकालय है , क्योंकि यह ऑब्जेक्ट-ओरिएंटेड है और इसमें स्ट्रक्चर्स के लिए कोई समर्थन नहीं है, इसे इसे दूसरे तरीके से लागू करना था, जो कि कुछ बदसूरत है। संरचनाओं को लागू करने वाली भाषाओं में वह विशेषता होती है ... अंतर्निहित। पास्कल या सी जैसी भाषाओं में एक उपयुक्त संरचना के साथ बिटमैप हेडर पढ़ने की कोशिश करें। यह आसान होगा (यदि संरचना का निर्माण और संयोजन किया गया है; पास्कल में आप रिकॉर्ड-आधारित पहुंच का उपयोग नहीं करेंगे लेकिन मनमाना बाइनरी डेटा पढ़ने के लिए कार्य करते हैं)। तो, फ़ाइलों और प्रत्यक्ष (स्थानीय) मेमोरी एक्सेस के लिए, संरचनाएं वस्तुओं की तुलना में बेहतर हैं। आज के लिए, हम JSON और XML के लिए उपयोग किए जाते हैं, और इसलिए हम बाइनरी फ़ाइलों के उपयोग को भूल जाते हैं (और एक साइड इफेक्ट के रूप में, स्ट्रक्चर्स का उपयोग)। लेकिन हाँ: वे मौजूद हैं, और उनका एक उद्देश्य है।

वे दुष्ट नहीं हैं। बस उन्हें सही उद्देश्य के लिए उपयोग करें।

यदि आप हथौड़ों के संदर्भ में सोचते हैं, तो आप शिकंजा को नाखूनों के रूप में मानेंगे, शिकंजा को खोजने के लिए दीवार में डुबाना कठिन होगा, और यह शिकंजा की गलती होगी, और वे बुरे लोग होंगे।


12

कल्पना कीजिए कि आपके पास 1,000,000 संरचनाओं की एक सरणी है। प्रत्येक संरचना बोली_प्राइस, ऑफ़र_प्राइस (शायद दशमलव) और इतने पर जैसे सामान के साथ एक इक्विटी का प्रतिनिधित्व करती है, यह C # / VB द्वारा बनाई गई है।

कल्पना कीजिए कि सरणी को मानव रहित ढेर में आवंटित मेमोरी के एक ब्लॉक में बनाया गया है ताकि कुछ अन्य देशी कोड थ्रेड समवर्ती (शायद कुछ उच्च-पूर्ण कोड गणित कर) एक्सेस कर सकें।

कल्पना करें कि C # / VB कोड मूल्य परिवर्तनों की एक बाजार फीड सुन रहा है, उस कोड को ऐरे के कुछ तत्व (जो भी सुरक्षा के लिए) तक पहुंचना पड़ सकता है और फिर कुछ मूल्य क्षेत्र (ओं) को संशोधित कर सकते हैं।

कल्पना कीजिए कि यह दसियों या प्रति सेकंड सैकड़ों बार भी किया जा रहा है।

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

ह्यूगो


3
यह सच है, लेकिन इस मामले में एक संरचना का उपयोग करने का कारण यह है कि ऐसा करने पर बाहरी बाधाओं (देशी कोड के उपयोग के द्वारा) के आवेदन डिजाइन पर लगाया जाता है। बाकी सब कुछ आप इन वस्तुओं के बारे में बताते हैं कि वे स्पष्ट रूप से C # या VB.NET में कक्षाएं होनी चाहिए।
जॉन हैना

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

9

जब कुछ को उत्परिवर्तित किया जा सकता है, तो यह पहचान की भावना प्राप्त करता है।

struct Person {
    public string name; // mutable
    public Point position = new Point(0, 0); // mutable

    public Person(string name, Point position) { ... }
}

Person eric = new Person("Eric Lippert", new Point(4, 2));

क्योंकि Personयह परिवर्तनशील है, एरिक की स्थिति को एरिक की क्लोनिंग से बदलने , क्लोन को स्थानांतरित करने और मूल को नष्ट करने के बारे में सोचना अधिक स्वाभाविक है । दोनों ऑपरेशन की सामग्री को बदलने में सफल होंगे eric.position, लेकिन एक दूसरे की तुलना में अधिक सहज है। इसी तरह, एरिक को संशोधित करने के तरीकों के लिए एरिक को (संदर्भ के रूप में) पास करना अधिक सहज है। एक विधि का एरिक का क्लोन देना लगभग हमेशा आश्चर्यचकित करने वाला है। किसी को भी म्यूट करने के Personलिए एक संदर्भ के लिए पूछना याद रखना चाहिए Personया वे गलत काम करेंगे।

यदि आप प्रकार को अपरिवर्तनीय बनाते हैं, तो समस्या दूर हो जाती है; अगर मैं संशोधित नहीं कर सकता eric, तो मुझे इससे कोई फ़र्क नहीं पड़ता कि मैं प्राप्त करता हूँ ericया कोई क्लोन eric। अधिक आम तौर पर, एक प्रकार मान से पारित करने के लिए सुरक्षित है यदि इसकी सभी अवलोकन योग्य स्थिति सदस्यों में होती है:

  • अडिग
  • संदर्भ प्रकार
  • मूल्य से पारित करने के लिए सुरक्षित है

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

एक अपरिवर्तनीय की गहनता इस Personबात पर निर्भर करती है कि आप क्या करने की कोशिश कर रहे हैं। यदि किसी व्यक्ति के बारे में डेटाPerson का एक सेट प्रस्तुत करता है , तो इसके बारे में कोई बात नहीं है; Personचर वास्तव में अमूर्त मूल्यों का प्रतिनिधित्व करते हैं , वस्तुओं का नहीं। (उस स्थिति में, इसका नाम बदलना शायद अधिक उचित होगा PersonData।) यदि Personवास्तव में खुद एक व्यक्ति मॉडलिंग कर रहा है, तो लगातार क्लोन बनाने और स्थानांतरित करने का विचार मूर्खतापूर्ण है, भले ही आप उस सोच के नुकसान से बच गए हों जिसे आप संशोधित कर रहे हैं। असली। उस मामले में यह संभवत: केवल बनाने के लिए अधिक स्वाभाविक होगाPerson एक संदर्भ प्रकार (यानी, एक वर्ग) ।

दी गई, जैसा कि कार्यात्मक प्रोग्रामिंग ने हमें सिखाया है कि सब कुछ अपरिवर्तनीय बनाने के लिए लाभ हैं (कोई भी चुपके से ericउसके संदर्भ में पकड़ नहीं सकता है और उसे म्यूट कर सकता है), लेकिन चूंकि यह ओओपी में मुहावरेदार नहीं है, इसलिए यह अभी भी अनजाने में किसी और के साथ काम करने वाला है। कोड।


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

1
यदि Thingएक उत्परिवर्तनीय वर्ग प्रकार है, तो एक वस्तु पहचान Thing[] को अतिक्रमण करेगा - चाहे कोई इसे चाहे या नहीं - जब तक कोई यह सुनिश्चित नहीं कर सकता कि कोई Thingभी सरणी जिसमें कोई बाहरी संदर्भ मौजूद नहीं है, कभी भी उत्परिवर्तित नहीं होगा। यदि कोई नहीं चाहता है कि सरणी तत्व पहचान को एनकैप्सुलेट करें, तो आम तौर पर यह सुनिश्चित करना चाहिए कि कोई भी आइटम जिसके लिए यह संदर्भ रखता है वह कभी भी उत्परिवर्तित होगा, या यह कि कोई बाहरी संदर्भ कभी भी मौजूद किसी भी आइटम के लिए मौजूद नहीं होगा [संकर दृष्टिकोण भी काम कर सकता है ]। न तो दृष्टिकोण बहुत सुविधाजनक है। यदि Thingकोई संरचना है, तो Thing[]केवल मानों को एनकैप्सुलेट करता है।
Supercat

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

6

इसका स्ट्रक्चर्स (और सी # के साथ नहीं, या तो) से कोई लेना-देना नहीं है, लेकिन जावा में आपको एक हैश मैप में कीज़ की तरह म्यूटेबल ऑब्जेक्ट्स की समस्या हो सकती है। यदि आप उन्हें एक मानचित्र में जोड़ने के बाद बदलते हैं और यह अपने हैश कोड को बदल देता है , तो बुरी चीजें हो सकती हैं।


3
यह सच है यदि आप एक मानचित्र में कुंजी के रूप में एक वर्ग का उपयोग करते हैं, तो भी।
मार्क Gravell

6

व्यक्तिगत रूप से जब मैं कोड को देखता हूं तो निम्नलिखित मेरे लिए बहुत अच्छा लगता है:

data.value.set (data.value.get () + 1);

इसके बजाय बस

data.value ++; या data.value = data.value + 1;

एक वर्ग के आसपास से गुजरते समय डेटा एनकैप्सुलेशन उपयोगी होता है और आप यह सुनिश्चित करना चाहते हैं कि मूल्य नियंत्रित फैशन में संशोधित हो। हालाँकि, जब आपने सार्वजनिक सेट किया है और ऐसे फ़ंक्शंस प्राप्त करते हैं, जो उस मूल्य से बहुत कम हैं जो कभी भी पारित हो जाता है, तो यह केवल सार्वजनिक डेटा संरचना को पास करने में सुधार कैसे होता है?

जब मैं एक वर्ग के अंदर एक निजी संरचना बनाता हूं, तो मैंने उस संरचना को एक समूह में एक चर का आयोजन करने के लिए बनाया। मैं कक्षा के दायरे में उस संरचना को संशोधित करने में सक्षम होना चाहता हूं, उस संरचना की प्रतियां प्राप्त नहीं कर सकता हूं और नए उदाहरण बना सकता हूं।

मेरे लिए यह सार्वजनिक चरों को व्यवस्थित करने के लिए उपयोग की जा रही संरचनाओं के एक वैध उपयोग को रोकता है, अगर मुझे अभिगम नियंत्रण चाहिए था तो मैं एक कक्षा का उपयोग करता।


1
सीधा मुद्दे पर! एक्सेस कंट्रोल प्रतिबंधों के बिना संरचनाएं संगठन इकाइयाँ हैं! दुर्भाग्य से, सी # ने उन्हें इस उद्देश्य के लिए बेकार कर दिया है!
थंडरग्रास

यह पूरी तरह से इस बिंदु को याद करता है क्योंकि आपके दोनों उदाहरण परस्पर परिवर्तन दिखाते हैं।
vidstige

सी # ने उन्हें इस उद्देश्य के लिए बेकार कर दिया क्योंकि यह संरचनाओं का उद्देश्य नहीं है
लुइज़ फेलिप

6

श्री एरिक लिपर्ट के उदाहरण के साथ कई मुद्दे हैं। यह उस बिंदु को स्पष्ट करने से वंचित है कि संरचना की नकल की जाती है और यदि आप सावधान नहीं हैं तो यह समस्या कैसे हो सकती है। उदाहरण को देखते हुए मैं इसे एक खराब प्रोग्रामिंग की आदत के रूप में देखता हूं और वास्तव में या तो संरचना या वर्ग के साथ कोई समस्या नहीं है।

  1. एक संरचना में केवल सार्वजनिक सदस्य होने चाहिए और उन्हें किसी भी तरह के एनकैप्सुलेशन की आवश्यकता नहीं होनी चाहिए। यदि ऐसा होता है तो यह वास्तव में एक प्रकार / वर्ग होना चाहिए। आपको वास्तव में एक ही बात कहने के लिए दो निर्माणों की आवश्यकता नहीं है।

  2. यदि आपके पास संरचना को संलग्न करने वाला वर्ग है, तो आप सदस्य संरचना को बदलने के लिए कक्षा में एक विधि कहेंगे। यह वही है जो मैं एक अच्छी प्रोग्रामिंग आदत के रूप में करूंगा।

एक उचित कार्यान्वयन निम्नानुसार होगा।

struct Mutable {
public int x;
}

class Test {
    private Mutable m = new Mutable();
    public int mutate()
    { 
        m.x = m.x + 1;
        return m.x;
    }
  }
  static void Main(string[] args) {
        Test t = new Test();
        System.Console.WriteLine(t.mutate());
        System.Console.WriteLine(t.mutate());
        System.Console.WriteLine(t.mutate());
    }

ऐसा लगता है कि यह प्रोग्रामिंग की आदत के साथ एक मुद्दा है जैसा कि संरचना के साथ एक मुद्दे के विपरीत है। संरचनाओं को परस्पर रूप से माना जाता है, यही विचार और इरादा है।

उम्मीद के मुताबिक व्यवहार में बदलाव का परिणाम:

1 2 3 जारी रखने के लिए किसी भी कुंजी को दबाएँ। । ।


4
अपरिवर्तनीय श्रेणी की वस्तुओं की तरह व्यवहार करने के लिए छोटे अपारदर्शी संरचनाओं को डिजाइन करने में कुछ भी गलत नहीं है; MSDN दिशानिर्देश तब उचित होते हैं जब कोई ऐसी चीज़ बनाने की कोशिश कर रहा होता है जो किसी वस्तु की तरह व्यवहार करती है । संरचनाएं कुछ मामलों में उपयुक्त होती हैं, जहां किसी को वस्तुओं की तरह व्यवहार करने वाली हल्की चीजों की जरूरत होती है, और ऐसे मामलों में जहां किसी को डक्ट टेप के साथ चिपक जाने वाले चर का एक गुच्छा चाहिए होता है। किसी कारण के लिए, हालांकि, कई लोग यह महसूस करने में विफल रहते हैं कि संरचनाओं के दो अलग-अलग उपयोग हैं, और एक के लिए उपयुक्त दिशा-निर्देश दूसरे के लिए अनुपयुक्त हैं।
सुपरकैट

5

परस्पर डेटा के कई फायदे और नुकसान हैं। मिलियन डॉलर का नुकसान अलियासिंग है। यदि एक ही मूल्य कई स्थानों पर उपयोग किया जा रहा है, और उनमें से एक इसे बदलता है, तो यह जादुई रूप से अन्य स्थानों पर परिवर्तित हो जाएगा जो इसका उपयोग कर रहे हैं। यह रेस स्थितियों से संबंधित नहीं बल्कि समान है।

मिलियन-डॉलर का फायदा कभी-कभी होता है। म्यूटेबल अवस्था आपको कोड से बदलती जानकारी को छिपाने की अनुमति दे सकती है जिसके बारे में जानने की आवश्यकता नहीं है।

इंटरप्रेटर की कला कुछ विस्तार से इन ट्रेड ऑफ में जाती है, और कुछ उदाहरण देती है।


सी # में संरचनाएं अलियास नहीं की जाती हैं। प्रत्येक संरचना असाइनमेंट एक प्रति है।
पुनरावर्ती

@recursive: कुछ मामलों में, यह परिवर्तनशील संरचनाओं का एक प्रमुख लाभ है, और जो मुझे इस धारणा पर सवाल खड़ा करता है कि संरचनाओं को परस्पर नहीं होना चाहिए। तथ्य यह है कि संकलक कभी कभी स्पष्ट रूप से प्रतिलिपि संरचनाओं को उत्परिवर्तित संरचनाओं की उपयोगिता को कम नहीं करता है।
सुपरकैट

1

मुझे विश्वास नहीं है कि अगर वे सही तरीके से उपयोग किए जाते हैं तो वे बुरे हैं। मैं इसे अपने उत्पादन कोड में नहीं डालूंगा, लेकिन मैं संरचित इकाई परीक्षण मॉक जैसी चीज़ के लिए कुछ करूँगा, जहां एक संरचना का जीवनकाल अपेक्षाकृत छोटा है।

एरिक उदाहरण का उपयोग करते हुए, शायद आप उस एरिक का दूसरा उदाहरण बनाना चाहते हैं, लेकिन समायोजन करें, क्योंकि यह आपके परीक्षण की प्रकृति है (यानी दोहराव, फिर संशोधन)। इससे कोई फर्क नहीं पड़ता कि एरिक के पहले उदाहरण के साथ क्या होता है यदि हम परीक्षण स्क्रिप्ट के शेष के लिए एरिक 2 का उपयोग कर रहे हैं, जब तक कि आप उसे परीक्षण तुलना के रूप में उपयोग करने की योजना नहीं बना रहे हैं।

यह विरासत कोड के परीक्षण या संशोधन के लिए ज्यादातर उपयोगी होगा जो उथले एक विशेष वस्तु (संरचनाओं के बिंदु) को परिभाषित करता है, लेकिन एक अपरिवर्तनीय संरचना होने से, यह इसे उपयोग करने से रोकता है।


जैसा कि मैं इसे देखता हूं, एक संरचना इसके दिल में है जो डक्ट टेप के साथ एक साथ चर चर का एक गुच्छा है। .NET की संरचना में डक्ट टेप के साथ चिपक गए चर के एक समूह के अलावा कुछ और होने का नाटक करना संभव है, और मैं यह सुझाव दूंगा कि जब व्यावहारिक प्रकार जो एक साथ अटके हुए चर के गुच्छा के अलावा कुछ और होने का नाटक करने वाला हो डक्ट टेप के साथ एक एकीकृत वस्तु के रूप में व्यवहार किया जाना चाहिए (जो एक संरचना के लिए अपरिवर्तनीयता होगी), लेकिन कभी-कभी डक्ट टेप के साथ चर का एक गुच्छा छड़ी करना उपयोगी होता है। उत्पादन कोड में भी, मैं एक प्रकार का होना बेहतर
समझूंगा

... जिसमें स्पष्ट रूप से "प्रत्येक क्षेत्र से संबंधित अंतिम चीज़ शामिल नहीं है" से परे कोई शब्दार्थ नहीं है, सभी शब्दार्थ को उस कोड में धकेल देते हैं जो संरचना का उपयोग करने की तुलना में अधिक संरचना करने की कोशिश करता है। उदाहरण के लिए, एक Range<T>प्रकार, सदस्यों Minimumऔर Maximumप्रकारों के क्षेत्रों Tऔर कोड के साथ Range<double> myRange = foo.getRange();, किसी भी गारंटी के बारे में कि क्या Minimumऔर Maximumजिसमें से आना चाहिए foo.GetRange();। बीत रहा है Rangeएक उजागर मैदान struct स्पष्ट है कि यह अपने आप में से किसी व्यवहार को जोड़ने के लिए नहीं जा रहा है होगा होना।
सुपरकैट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.