आप एक वर्ग के बजाय एक संरचना का उपयोग कब करते हैं? [बन्द है]


174

कक्षाओं बनाम कक्षाओं का उपयोग करने के लिए अंगूठे के आपके नियम क्या हैं? मैं उन शर्तों की C # परिभाषा के बारे में सोच रहा हूं, लेकिन अगर आपकी भाषा में भी ऐसी अवधारणाएं हैं, तो मैं आपकी राय भी सुनना चाहूंगा।

मैं लगभग हर चीज के लिए कक्षाओं का उपयोग करता हूं, और केवल तभी उपयोग करता हूं जब कोई चीज बहुत सरल होती है और उसका मूल्य प्रकार होना चाहिए, जैसे कि PhoneNumber या ऐसा कुछ। लेकिन यह अपेक्षाकृत मामूली उपयोग की तरह लगता है और मुझे उम्मीद है कि अधिक दिलचस्प उपयोग के मामले हैं।



7
@ फ़र्स्टडर्ड किसी अन्य साइट पर किसी चीज़ के डुप्लिकेट के रूप में एक प्रश्न को चिह्नित नहीं कर सकता है, हालांकि यह निश्चित रूप से संबंधित है।
एडम लीयर

@ एना लियर: मुझे पता है, मैंने करीब-डुप्लिकेट के रूप में नहीं प्रवास करने के लिए मतदान किया था। एक बार जब यह माइग्रेट हो जाता है, तो इसे डुप्लिकेट के रूप में ठीक से बंद किया जा सकता है
FrustratedWithFormsDesigner

5
@ मुझे विश्वास नहीं हुआ कि यह माइग्रेट किए जाने की आवश्यकता है।
एडम लेअर

15
ऐसा लगता है कि P.SE बनाम SO के लिए अधिक अनुकूल प्रश्न है। यही कारण है कि मैंने इसे यहां पूछा।
रेशनलगीक

जवाबों:


169

पालन ​​करने का सामान्य नियम यह है कि संबंधित संपत्तियों के छोटे, सरल (एक-स्तरीय) संग्रह होने चाहिए, जो एक बार बनाए जाने के बाद अपरिवर्तनीय हैं; कुछ और के लिए, एक वर्ग का उपयोग करें।

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

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

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

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

यह और भी भ्रामक हो जाता है यदि आपको अपनी संरचना के सदस्य के रूप में एक संदर्भ प्रकार होना चाहिए (अस्वीकृत नहीं है, लेकिन वस्तुतः सभी मामलों में बहुत बुरा अभ्यास); वर्ग क्लोन नहीं किया जाएगा (केवल संरचना का संदर्भ), इसलिए संरचना में परिवर्तन मूल वस्तु को प्रभावित नहीं करेगा, लेकिन संरचना के उपवर्ग में परिवर्तन कॉल कोड से आवृत्ति को प्रभावित करेगा। यह बहुत आसानी से बहुत असंगत राज्यों में उत्परिवर्तनीय संरचनाएं डाल सकता है जो वास्तविक समस्या है जहां से दूर एक लंबा रास्ता तय कर सकते हैं।

इस कारण से, C # पर प्रत्येक प्राधिकरण हमेशा आपकी संरचनाओं को अपरिवर्तनीय बनाने के लिए कहता है; उपभोक्ता को केवल एक वस्तु के निर्माण पर गुणों के मूल्यों को निर्दिष्ट करने की अनुमति दें, और उस उदाहरण के मूल्यों को बदलने के लिए कभी कोई साधन प्रदान न करें। पठनीय क्षेत्र, या केवल गुण प्राप्त करें, नियम हैं। यदि उपभोक्ता मूल्य को बदलना चाहता है, तो वे पुराने के मूल्यों के आधार पर एक नई वस्तु बना सकते हैं, जो वे चाहते हैं, या वे एक विधि कह सकते हैं, जो ऐसा ही करेगा। यह उन्हें आपकी संरचना के एक एकल उदाहरण को एक वैचारिक "मूल्य" के रूप में व्यवहार करने के लिए मजबूर करता है, जो अन्य सभी से अविभाज्य और अलग है (लेकिन संभवतः समान है)। यदि वे आपके प्रकार द्वारा संग्रहीत "मान" पर एक ऑपरेशन करते हैं, तो उन्हें एक नया "मान" मिलता है जो उनके प्रारंभिक मूल्य से अलग होता है,

एक अच्छे उदाहरण के लिए, DateTime प्रकार देखें। आप किसी भी समय तिथि उदाहरण के किसी भी क्षेत्र को सीधे असाइन नहीं कर सकते हैं; आपको या तो एक नया बनाना होगा, या मौजूदा एक विधि पर कॉल करना होगा जो एक नया उदाहरण उत्पन्न करेगा। इसका कारण यह है कि दिनांक और समय संख्या 5 की तरह एक "मान" है, और संख्या 5 में एक नए मूल्य में परिवर्तन होता है जो 5. नहीं है। सिर्फ इसलिए कि 5 + 1 = 6 का अर्थ 5 नहीं है अब 6 है क्योंकि आपने इसमें 1 जोड़ा है। डेटटाइम्स उसी तरह काम करते हैं; 12:00 "मिनट" नहीं बनता है 12:01 यदि आप एक मिनट जोड़ते हैं, तो आपको इसके बजाय एक नया मान मिलता है 12:01 जो 12:00 से अलग है। यदि यह आपके प्रकार के लिए एक तार्किक स्थिति है (अच्छा वैचारिक उदाहरण जो .NET में निर्मित नहीं हैं, तो पैसे, दूरी, वजन और UOM की अन्य मात्राएं हैं जहां संचालन को मूल्य के सभी भागों को ध्यान में रखना चाहिए) फिर एक संरचना का उपयोग करें और तदनुसार डिजाइन करें। अधिकांश अन्य मामलों में जहां किसी वस्तु के उप-आइटम स्वतंत्र रूप से परस्पर योग्य होने चाहिए, एक वर्ग का उपयोग करें।


1
अच्छा उत्तर! मैं इसे 'डोमेन ड्रिवेन डेवलपमेंट' कार्यप्रणाली और पुस्तक को पढ़ने के लिए इंगित करता हूँ, जो आपकी राय से कुछ हद तक संबंधित है कि किसी को कक्षाओं या संरचनाओं का उपयोग किस अवधारणा के आधार पर करना चाहिए जिसे आप वास्तव में लागू करने की कोशिश कर रहे हैं
मैक्सिम पोपरावको

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

1
@ लॉरेंटबोरगौल्ट-रॉय - सच। अन्य गोच भी हैं; उदाहरण के लिए, संरचना में वंशानुगत पदानुक्रम नहीं हो सकता है। वे इंटरफेस को लागू कर सकते हैं, लेकिन System.ValueType (स्पष्ट रूप से उनके संरचित होने के कारण) के अलावा किसी अन्य चीज़ से प्राप्त नहीं कर सकते हैं।
कीथ्स

एक जावास्क्रिप्ट देव के रूप में मुझे दो बातें पूछनी हैं। वस्तु शाब्दिक कैसे होते हैं जो कि विशिष्ट संरचना से अलग होते हैं और यह क्यों महत्वपूर्ण है कि संरचना अपरिवर्तनीय हो सकती है?
एरिक रेपेन

1
@ErikReppen: आपके दोनों प्रश्नों के उत्तर में: जब किसी संरचना को किसी फ़ंक्शन में पास किया जाता है, तो उसे मान द्वारा पास किया जाता है। एक वर्ग के साथ, आप कक्षा के संदर्भ में (अभी भी मूल्य से) गुजर रहे हैं। एरिक लिपर्ट चर्चा करते हैं कि यह एक समस्या क्यों है: blogs.msdn.com/b/ericlippert/archive/2008/05/14/… । कीथ का अंतिम पैराग्राफ भी इन सवालों को कवर करता है।
ब्रायन

14

उत्तर: "शुद्ध डेटा निर्माण के लिए एक संरचना का उपयोग करें, और संचालन के साथ वस्तुओं के लिए एक वर्ग" निश्चित रूप से गलत आईएमओ है। यदि कोई संरचना बड़ी संख्या में गुण रखती है तो एक वर्ग लगभग हमेशा अधिक उपयुक्त होता है। Microsoft अक्सर एक दक्षता के दृष्टिकोण से कहता है, यदि आपका प्रकार 16 बाइट्स से बड़ा है तो यह एक वर्ग होना चाहिए।

https://stackoverflow.com/questions/1082311/why-should-a-net-struct-be-less-than-16-bytes


1
पूर्ण रूप से। अगर मैं कर सकता तो मैं upvote करता। मुझे नहीं लगता कि यह विचार कहां से आया है कि संरचनाएं डेटा के लिए हैं और ऑब्जेक्ट नहीं हैं। C # में एक संरचना केवल स्टैक पर आवंटित करने के लिए एक तंत्र है। ऑब्जेक्ट पॉइंटर की एक प्रति के बजाय पूरे ढांचे की प्रतियां चारों ओर से पारित की जाती हैं।
mike30 17

2
@mike - C # स्ट्रक्चर्स को स्टैक पर आवश्यक रूप से आवंटित नहीं किया गया है।
ली

1
@ माइक "संरचनाएं डेटा के लिए हैं" विचार संभवतः सी प्रोग्रामिंग के कई वर्षों से अधिग्रहित आदत से आता है। C की कोई कक्षा नहीं है और इसकी संरचनाएं केवल डेटा कंटेनर हैं।
कोनामिमन

जाहिर है कि माइकल के के जवाब को पढ़ने के लिए कम से कम 3 सी प्रोग्रामर (उठे हुए) हैं; ;-)
'16:

10

निम्नलिखित स्थितियों में ए classऔर ए के बीच सबसे महत्वपूर्ण अंतर structहै:

  बात बात 1 = नई बात ();
  thing1.somePropertyOrField = 5;
  बात 2 = बात 1;
  thing2.somePropertyOrField = 9;

अंतिम कथन का प्रभाव क्या होना चाहिए thing1.somePropertyOrField? यदि Thingएक संरचना, और somePropertyOrFieldएक उजागर सार्वजनिक क्षेत्र है, तो वस्तुएं thing1और thing2एक दूसरे से "अलग" हो जाएंगी, इसलिए बाद वाला बयान प्रभावित नहीं करेगा thing1। यदि Thingएक वर्ग है, तो thing1और thing2एक दूसरे के साथ संलग्न किया है, और इसलिए बाद के बयान लिखेंगे thing1.somePropertyOrField। उन मामलों में एक संरचना का उपयोग करना चाहिए जहां पूर्व शब्दार्थ अधिक समझ में आता है, और उन मामलों में एक वर्ग का उपयोग करना चाहिए जहां बाद के शब्दार्थ अधिक समझ में आते हैं।

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

उदाहरण के लिए, कथनों पर विचार करें:

  व्यक्ति somePerson = myPeople.GetPerson ("123-45-6789");
  somePerson.Name = "मैरी जॉनसन"; // था "मैरी स्मिथ"

क्या दूसरा विवरण संग्रहीत जानकारी को बदल देगा myPeople? यदि Personएक उजागर क्षेत्र संरचना है, यह नहीं होगा, और यह तथ्य यह है कि यह एक उजागर क्षेत्र संरचना होने का एक स्पष्ट परिणाम नहीं होगा ; अगर Personएक संरचना है और एक अद्यतन करना चाहता है myPeople, तो स्पष्ट रूप से कुछ करना होगा myPeople.UpdatePerson("123-45-6789", somePerson)। यदि Personएक वर्ग है, हालांकि, यह निर्धारित करना बहुत कठिन हो सकता है कि उपरोक्त कोड कभी भी सामग्री को अपडेट नहीं करेगा MyPeople, हमेशा इसे अपडेट करेगा, या कभी-कभी इसे अपडेट कर सकता है।

इस धारणा के संबंध में कि संरचना "अपरिवर्तनीय" होनी चाहिए, मैं सामान्य रूप से असहमत हूं। "अपरिवर्तनीय" संरचना (जहां एक निर्माणकर्ता में अदृश्यता लागू की जाती है) के लिए वैध उपयोग के मामले हैं, लेकिन यह आवश्यक है कि किसी भी समय किसी भी हिस्से को फिर से लिखा जाना चाहिए, जो परिवर्तनशील, बेकार, और अधिक उपयुक्त होने के कारण बग को उजागर करने के लिए अधिक उपयुक्त होगा। सीधे खेत। उदाहरण के लिए, एक पर विचार PhoneNumber, struct जिसका क्षेत्रों दूसरों के बीच में शामिल हैं, AreaCodeऔर Exchange, और लगता है कि एक एक है List<PhoneNumber>। निम्नलिखित का प्रभाव बहुत स्पष्ट होना चाहिए:

  for (int i = 0; मैं <myList.Count; i ++)
  {
    फोननंबर नम्बर = मायलिस्ट [i];
    अगर (.Number.AreaCode == "312")
    {
      string newExchange = "";
      अगर (new312to708Exchanges.TryGetValue (theNumber.Exchange), newExhange से बाहर)
      {
        theNumber.AreaCode = "708";
        .Number.Exchange = newExchange;
        myList [i] = नम्बर;
      }
    }
  }

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

बीटीडब्ल्यू, कम से कम दो मामले हैं जहां संरचनात्मक रूप से उत्परिवर्तनीय प्रकारों के संदर्भ हो सकते हैं।

  1. संरचना के शब्दार्थ से संकेत मिलता है कि यह वस्तु के गुणों को रखने के लिए शॉर्टकट के बजाय प्रश्न में वस्तु की पहचान को संग्रहीत कर रहा है। उदाहरण के लिए, एक `KeyValuePair` कुछ बटन की पहचान रखता है, लेकिन उन बटन की स्थिति, हाइलाइट स्टेट्स आदि के बारे में लगातार जानकारी रखने की उम्मीद नहीं की जाएगी।
  2. संरचना को पता है कि यह उस वस्तु का एकमात्र संदर्भ रखता है, किसी और को एक संदर्भ नहीं मिलेगा, और किसी भी उत्परिवर्तन जो उस वस्तु के लिए कभी भी किया जाएगा, यह संदर्भ कहीं भी संग्रहीत होने से पहले किया जाएगा।

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


7

मैं केवल तभी जब किसी एक विधि की आवश्यकता होती है, तब मैं संरचना का उपयोग करता हूं, इस प्रकार एक वर्ग "भारी" लगता है। इस प्रकार कभी-कभी मैं हल्के वस्तुओं के रूप में संरचना का इलाज करता हूं। सावधान: यह हमेशा काम नहीं करता है और हमेशा करने के लिए सबसे अच्छी बात नहीं है, इसलिए यह वास्तव में स्थिति पर निर्भर करता है!

अतिरिक्त संसाधन

अधिक औपचारिक स्पष्टीकरण के लिए, MSDN कहता है: http://msdn.microsoft.com/en-us/library/ms229017.aspx यह लिंक सत्यापित करता है कि मैंने ऊपर जो उल्लेख किया है, केवल डेटा की थोड़ी मात्रा में आवास के लिए संरचनाओं का उपयोग किया जाना चाहिए।


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

2
@ एना लीयर धन्यवाद मुझे बताने के लिए, अब से ध्यान में रखना होगा!

2
-1 "मैं केवल तभी उपयोग करता हूं जब एक विधि की आवश्यकता होती है, इस प्रकार एक वर्ग बहुत भारी लगता है।" ... भारी? उसका वास्तव में क्या अर्थ है? ... और क्या आप वास्तव में कह रहे हैं कि बस एक ही विधि है तो यह संभवतः एक संरचना होनी चाहिए?
१४:०६

2

शुद्ध डेटा निर्माण के लिए एक संरचना का उपयोग करें, और संचालन के साथ वस्तुओं के लिए एक वर्ग।

यदि आपकी डेटा संरचना को किसी एक्सेस कंट्रोल की आवश्यकता नहीं है और इसमें गेट / सेट के अलावा कोई विशेष ऑपरेशन नहीं है, तो एक स्ट्रक्चर का उपयोग करें। इससे यह स्पष्ट हो जाता है कि यह सभी संरचना डेटा के लिए एक कंटेनर है।

यदि आपकी संरचना में ऐसे ऑपरेशन हैं जो संरचना को किसी भी अधिक जटिल तरीके से संशोधित करते हैं, तो एक वर्ग का उपयोग करें। एक वर्ग अन्य वर्गों के साथ तर्कों के माध्यम से बातचीत कर सकता है।

इसे ईंट और हवाई जहाज के बीच का अंतर समझें। एक ईंट एक संरचना है; इसकी लंबाई, चौड़ाई और ऊंचाई है। यह बहुत कुछ नहीं कर सकता। एक हवाई जहाज में कई ऑपरेशन हो सकते हैं जो इसे किसी भी तरह से बदल सकते हैं, जैसे कि नियंत्रण सतहों और इंजन के जोर को बदलना। एक वर्ग के रूप में बेहतर होगा।


2
"शुद्ध डेटा निर्माण के लिए एक संरचना का उपयोग करें, और ऑपरेशन के साथ वस्तुओं के लिए एक वर्ग" सी # में यह बकवास है। नीचे मेरा जवाब देखें।
बट्टेव

1
"शुद्ध डेटा निर्माण के लिए एक संरचना का उपयोग करें, और संचालन के साथ वस्तुओं के लिए एक वर्ग।" यह C / C ++ के लिए सही है, C #
taktak004
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.