संरचना का उपयोग कब करें?


1390

जब आप संरचना का उपयोग करना चाहिए और C # में वर्ग नहीं होना चाहिए? मेरा वैचारिक मॉडल यह है कि समय का उपयोग तब किया जाता है जब आइटम केवल मूल्य प्रकारों का एक संग्रह होता है । तार्किक रूप से उन सभी को एक साथ मिलकर एक सामंजस्यपूर्ण संपूर्ण बनाने का तरीका।

मैं यहाँ इन नियमों में आया :

  • एक संरचना को एक एकल मूल्य का प्रतिनिधित्व करना चाहिए।
  • एक संरचना में 16 बाइट्स से कम का मेमोरी फ़ुटप्रिंट होना चाहिए।
  • निर्माण के बाद एक संरचना को नहीं बदला जाना चाहिए।

क्या ये नियम काम करते हैं? एक संरचना का अर्थ क्या है?


247
System.Drawing.Rectangleइन तीनों नियमों का उल्लंघन करता है।
क्रिस डब्ल्यूडब्ल्यू

4
C # में काफी कम व्यावसायिक खेल लिखे गए हैं, मुद्दा यह है कि उनका उपयोग अनुकूलित कोड के लिए किया जाता है
BlackTigerX

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

6
@ एरिकफोर्ब्स: मुझे लगता है कि यह आमतौर पर सबसे बड़े बीसीएल "

4
@ क्रिस मैं देख रहा हूं, लेकिन क्या वे मूल्य एक आयत का प्रतिनिधित्व नहीं करते हैं, जो "एकल" मूल्य है? वेक्टर 3 डी या कलर की तरह, वे भी अंदर कई मान हैं, लेकिन मुझे लगता है कि वे एकल मूल्यों का प्रतिनिधित्व करते हैं?
मार्सन माओ

जवाबों:


604

ओपी द्वारा संदर्भित स्रोत में कुछ विश्वसनीयता है ... लेकिन Microsoft के बारे में क्या है - संरचना उपयोग पर रुख क्या है? मैंने Microsoft से कुछ अतिरिक्त सीखने की कोशिश की , और यहाँ मैंने पाया:

एक वर्ग के बजाय एक संरचना को परिभाषित करने पर विचार करें यदि प्रकार के उदाहरण छोटे और आमतौर पर अल्पकालिक हैं या आमतौर पर अन्य वस्तुओं में एम्बेडेड हैं।

किसी संरचना को तब तक परिभाषित न करें जब तक कि सभी प्रकार में निम्नलिखित विशेषताएं न हों:

  1. यह तार्किक रूप से आदिम प्रकारों (पूर्णांक, डबल, और इसी तरह) के समान एकल मूल्य का प्रतिनिधित्व करता है।
  2. इसका एक उदाहरण आकार 16 बाइट्स से छोटा है।
  3. यह अपरिवर्तनीय है।
  4. इसे बार-बार बॉक्सिंग नहीं करना पड़ेगा।

Microsoft लगातार उन नियमों का उल्लंघन करता है

ठीक है, # 2 और # 3 वैसे भी। हमारे प्रिय शब्दकोश में 2 आंतरिक संरचनाएं हैं:

[StructLayout(LayoutKind.Sequential)]  // default for structs
private struct Entry  //<Tkey, TValue>
{
    //  View code at *Reference Source
}

[Serializable, StructLayout(LayoutKind.Sequential)]
public struct Enumerator : 
    IEnumerator<KeyValuePair<TKey, TValue>>, IDisposable, 
    IDictionaryEnumerator, IEnumerator
{
    //  View code at *Reference Source
}

* संदर्भ स्रोत

'JonnyCantCode.com' स्रोत को 4 में से 3 मिले - # 4 के बाद से काफी क्षमा करने योग्य शायद एक मुद्दा नहीं होगा। यदि आप अपने आप को एक संरचना बॉक्सिंग पाते हैं, तो अपनी वास्तुकला पर पुनर्विचार करें।

आइए देखें कि Microsoft इन संरचनाओं का उपयोग क्यों करेगा:

  1. प्रत्येक संरचना, EntryऔरEnumerator , एकल मूल्यों का प्रतिनिधित्व करते हैं।
  2. गति
  3. Entryडिक्शनरी क्लास के बाहर कभी भी एक पैरामीटर के रूप में पारित नहीं किया जाता है। आगे की जांच से पता चलता है कि IEnumerable के कार्यान्वयन को संतुष्ट करने के लिए, डिक्शनरी उस Enumeratorसंरचना का उपयोग करती है, जो हर बार एक एन्यूमरेटर का अनुरोध करने पर ... समझ में आता है।
  4. शब्दकोश वर्ग के लिए आंतरिक। Enumeratorसार्वजनिक है क्योंकि डिक्शनरी एन्युमरेबल है और IEnumerator इंटरफ़ेस कार्यान्वयन के लिए समान पहुँच होनी चाहिए - जैसे IEnumerator गेटर।

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

जो कुछ हम यहां नहीं देखते हैं, वह केवल 16 बाइट्स या इंस्टेंस आकार को बनाए रखने के लिए किसी भी प्रयास या सबूत का है।

  1. ऊपर की संरचना में कुछ भी घोषित नहीं किया गया है readonly- अपरिवर्तनीय नहीं
  2. इन संरचना का आकार 16 बाइट्स से अधिक हो सकता है
  3. Entryएक अनिर्धारित जीवन है (से Add(), करने के लिए Remove(), Clear()या कचरा संग्रहण);

और ... 4. दोनों संरचनाएं TKey और TValue को संग्रहीत करती हैं, जो हम सभी जानते हैं कि संदर्भ प्रकार होने में काफी सक्षम हैं (अतिरिक्त बोनस जानकारी)

हशेड कीज़ के बावजूद, शब्दकोश भाग में तेज़ होते हैं क्योंकि एक संरचना को भेजना एक संदर्भ प्रकार की तुलना में तेज़ होता है। यहां, मेरे पास Dictionary<int, int>300,000 यादृच्छिक पूर्णांकों को क्रमिक रूप से बढ़ी हुई कुंजियों के साथ संग्रहीत किया गया है।

क्षमता: 312874
याद: 2660827 बाइट्स
पूर्ण आकार: 5ms
भरने के लिए कुल समय: 889 मिलियन

क्षमता : आंतरिक सरणी से पहले उपलब्ध तत्वों की संख्या का आकार परिवर्तन होना चाहिए।

MemSize : एक मेमोरीस्ट्रीम में शब्दकोश क्रमबद्ध करने और एक बाइट लंबाई (हमारे उद्देश्यों के लिए पर्याप्त सटीक) प्राप्त करने से निर्धारित होता है।

पूर्ण आकार : 150862 तत्वों से 312874 तत्वों तक आंतरिक सरणी का आकार बदलने में लगने वाला समय। जब आप जानते हैं कि प्रत्येक तत्व क्रमिक रूप से कॉपी किया गया है Array.CopyTo(), तो वह बहुत जर्जर नहीं है।

भरने के लिए कुल समय : लॉगिंग और OnResizeस्रोत पर जोड़े गए एक घटना के कारण, मोटे तौर पर तिरछा ; हालांकि, ऑपरेशन के दौरान 15 बार आकार बदलते हुए 300k पूर्णांक भरने के लिए अभी भी प्रभावशाली है। जिज्ञासा से बाहर, भरने के लिए कुल समय क्या होगा यदि मैं पहले से ही क्षमता जानता था? 13ms

तो, अब, अगर Entryएक वर्ग थे? क्या ये समय या मैट्रिक्स वास्तव में इतना भिन्न होगा?

क्षमता: 312874
याद: 2660827 बाइट्स
पूर्ण आकार: 26ms
भरने के लिए कुल समय: 964 मिलियन

जाहिर है, बड़ा अंतर आकार बदलने में है। कोई फर्क अगर शब्दकोश क्षमता के साथ आरंभ किया जाता है? पर्याप्त नहीं होने के साथ संबंधित ... 12ms

क्या होता है, क्योंकि Entryएक संरचना है, इसे संदर्भ प्रकार की तरह आरंभीकरण की आवश्यकता नहीं है। यह मूल्य प्रकार की सुंदरता और प्रतिबंध दोनों है। Entryसंदर्भ प्रकार के रूप में उपयोग करने के लिए , मुझे निम्नलिखित कोड डालना होगा:

/*
 *  Added to satisfy initialization of entry elements --
 *  this is where the extra time is spent resizing the Entry array
 * **/
for (int i = 0 ; i < prime ; i++)
{
    destinationArray[i] = new Entry( );
}
/*  *********************************************** */  

कारण यह है कि मुझे Entryसंदर्भ प्रकार के रूप में प्रत्येक सरणी तत्व को एमएसडीएन में पाया जा सकता है : संरचना डिजाइन । संक्षेप में:

एक संरचना के लिए एक डिफ़ॉल्ट निर्माता प्रदान न करें।

यदि कोई संरचना डिफॉल्ट कंस्ट्रक्टर को परिभाषित करती है, जब संरचना की सरणियाँ बनाई जाती हैं, तो सामान्य भाषा रनटाइम प्रत्येक ऐरे एलिमेंट पर डिफ़ॉल्ट कंस्ट्रक्टर को स्वचालित रूप से निष्पादित करता है।

कुछ कंपाइलर, जैसे कि C # कंपाइलर, डिफॉल्ट कंस्ट्रक्टर वाले स्ट्रक्चर को अनुमति नहीं देते हैं।

यह वास्तव में काफी सरल है और हम असिमोव के रोबोटिक्स के थ्री लॉज़ से उधार लेंगे :

  1. उपयोग करने के लिए संरचना सुरक्षित होनी चाहिए
  2. जब तक यह नियम # 1 का उल्लंघन नहीं करेगा, तब तक संरचना को अपना कार्य कुशलतापूर्वक करना चाहिए
  3. जब तक इसके नियम # 1 को पूरा करने के लिए इसके विनाश की आवश्यकता नहीं है, तब तक इसके उपयोग के दौरान संरचना बरकरार रहनी चाहिए

... हम इस से क्या लेते हैं : संक्षेप में, मूल्य प्रकारों के उपयोग के साथ जिम्मेदार हैं। वे त्वरित और कुशल हैं, लेकिन कई अप्रत्याशित व्यवहारों को पैदा करने की क्षमता रखते हैं अगर ठीक से रखरखाव नहीं किया जाता है (यानी अनजाने में प्रतियां)।


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


2
यह तथ्य कि Microsoft के कई प्रकार उन नियमों का उल्लंघन करते हैं, उन प्रकारों के साथ समस्या का प्रतिनिधित्व नहीं करते हैं, बल्कि यह इंगित करते हैं कि नियम सभी संरचना प्रकारों पर लागू नहीं होने चाहिए। यदि कोई संरचना एकल इकाई का प्रतिनिधित्व करती है [जैसा कि Decimalया DateTimeउसके साथ ], तो यदि वह अन्य तीन नियमों का पालन नहीं करती है, तो उसे एक वर्ग द्वारा प्रतिस्थापित किया जाना चाहिए। यदि कोई संरचना चर का एक निश्चित संग्रह रखती है, जिसमें से प्रत्येक का कोई भी मूल्य हो सकता है जो उसके प्रकार [जैसे Rectangle] के लिए मान्य होगा , तो उसे अलग-अलग नियमों का पालन ​​करना चाहिए , जिनमें से कुछ "एकल-मूल्य" संरचना के विपरीत हैं। ।
सुपरकैट

4
@ आईब्रेट: कुछ लोग Dictionaryप्रवेश के प्रकार को इस आधार पर सही ठहराते हैं कि यह केवल एक आंतरिक प्रकार है, प्रदर्शन शब्दार्थ या किसी अन्य बहाने से अधिक महत्वपूर्ण माना जाता था। मेरा कहना है कि एक प्रकार की तरह Rectangleइसकी सामग्री को व्यक्तिगत रूप से-संपादन योग्य क्षेत्रों के रूप में उजागर किया जाना चाहिए "क्योंकि" प्रदर्शन के परिणामस्वरूप सिमेंटिक खामियों को दूर किया जाता है, लेकिन क्योंकि प्रकार शब्दार्थ स्वतंत्र मूल्यों के एक निश्चित सेट का प्रतिनिधित्व करता है , और इसलिए पारस्परिक संरचना दोनों है। अधिक प्रदर्शन और शब्दार्थ से बेहतर
सुपरकैट

2
@ सुपरकैट: मैं सहमत हूं ... और मेरे जवाब का पूरा बिंदु यह था कि 'दिशा-निर्देश' बहुत कमजोर हैं और व्यवहारों की पूरी जानकारी और समझ के साथ इसका इस्तेमाल किया जाना चाहिए। यहाँ पर मेरे जवाब
दयनीय

154

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


1
वह केवल एक "कैविएट" है। मूल्य-प्रकार और मामलों के "उठाने" पर भी विचार करना चाहिए जैसे (Guid)null(संदर्भ-प्रकार के लिए एक अशक्त डालना ठीक है), अन्य बातों के अलावा।

1
C / C ++ से अधिक महंगा है? C ++ में अनुशंसित तरीका है कि वस्तुओं को मान द्वारा पारित किया जाए
आयन टोडरेल

@IonTodirel प्रदर्शन के बजाय स्मृति सुरक्षा कारणों से नहीं था? यह हमेशा एक ट्रेड-ऑफ है, लेकिन स्टैक द्वारा 32 बी पास करना हमेशा (टीएम) रजिस्टर द्वारा 4 बी संदर्भ पारित करने की तुलना में धीमी गति से होने वाला है। हालाँकि , यह भी ध्यान दें कि "मान / संदर्भ" का उपयोग C # और C ++ में थोड़ा भिन्न है - जब आप किसी ऑब्जेक्ट के संदर्भ में पास करते हैं, तब भी आप मूल्य से गुजर रहे होते हैं, भले ही आप एक संदर्भ (आप) पास कर रहे हों संदर्भ का मूल्य फिर से पास करना, संदर्भ का संदर्भ नहीं, मूल रूप से)। यह मूल्य शब्दार्थ नहीं है , लेकिन यह तकनीकी रूप से "पास-बाय-वैल्यू" है।
लुआएन

@ लुअन कॉपी करना लागत का केवल एक पहलू है। सूचक / संदर्भ के कारण अतिरिक्त अप्रत्यक्षता भी प्रति उपयोग की लागत है। कुछ मामलों में संरचना को स्थानांतरित भी किया जा सकता है और इस प्रकार इसे कॉपी करने की भी आवश्यकता नहीं होती है।
ओनूर

@ यह दिलचस्प है। नकल के बिना आप "चाल" कैसे करते हैं? मुझे लगा कि asm "mov" निर्देश वास्तव में "चाल" नहीं है। यह कॉपी करता है।
विंगर सेंडोन

148

मैं मूल पद में दिए गए नियमों से सहमत नहीं हूँ। यहाँ मेरे नियम हैं:

1) आप सरणियों में संग्रहीत होने पर प्रदर्शन के लिए संरचनाओं का उपयोग करते हैं। (यह भी देखें कि उत्तर कब देते हैं? )

2) आपको उन्हें C / C ++ से संरचित डेटा पास करने की आवश्यकता है

3) जब तक आपको उनकी आवश्यकता न हो, तब तक पैटर्न का उपयोग न करें:

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

4
+1 हां, मैं पूरी तरह से # 1 पर सहमत हूं (यह एक बहुत बड़ा लाभ है जब छवियों, आदि जैसी चीजों से निपटना) और यह इंगित करने के लिए कि वे "सामान्य वस्तुओं" से अलग हैं और मौजूदा ज्ञान के अलावा इसे जानने का तरीका है या स्वयं प्रकार की जांच करना। इसके अलावा, आप एक संरचना प्रकार के लिए एक शून्य मान नहीं डाल सकते हैं :-) यह वास्तव में एक मामला है जहां मैं लगभग चाहता हूं कि गैर-कोर मूल्य-प्रकार के लिए कुछ 'हंगेरियन' हो या चर घोषणा स्थल पर एक अनिवार्य 'संरचना' कीवर्ड हो। ।

@pst: यह सच है कि किसी को कुछ जानना है, structयह जानना है कि यह कैसे व्यवहार करेगा, लेकिन अगर कुछ structउजागर क्षेत्रों के साथ है, तो यह सब जानना होगा। यदि कोई वस्तु एक उजागर-क्षेत्र-संरचना प्रकार की संपत्ति को उजागर करती है, और यदि कोड उस संरचना को एक चर में पढ़ता है और संशोधित करता है, तो कोई भी सुरक्षित रूप से भविष्यवाणी कर सकता है कि इस तरह की कार्रवाई उस वस्तु को प्रभावित नहीं करेगी जिसकी संपत्ति तब तक पढ़ी गई थी जब तक कि संरचना लिखी नहीं गई थी वापस। इसके विपरीत, यदि संपत्ति एक उत्परिवर्ती वर्ग प्रकार की थी, तो इसे पढ़ना और इसे संशोधित करना अंतर्निहित वस्तु को अपेक्षित रूप से अद्यतन कर सकता है, लेकिन ...
सुपरकैट

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

# 1 के साथ हाजिर। संरचना से भरी सूची ऑब्जेक्ट संदर्भों से भरी सूची (सही आकार की संरचना के लिए) की तुलना में L1 / L2 कैश में अधिक प्रासंगिक डेटा को निचोड़ सकती है।
मैट स्टीफेंसन

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

87

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

संपादित करें

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

यदि आपको संदर्भ शब्दार्थ की आवश्यकता है तो आपको एक वर्ग की आवश्यकता है न कि एक संरचना की।


13
हर कोई जानता है कि। लगता है जैसे वह "संरचना एक मूल्य प्रकार" से अधिक की तलाश में है।
TheSmurf

21
यह सबसे बुनियादी मामला है और इस पोस्ट को पढ़ने वाले और यह नहीं जानता कि किसी के लिए भी कहा जाना चाहिए।
जोशबर्क

3
ऐसा नहीं है कि यह उत्तर सत्य नहीं है; यह स्पष्ट रूप से है यह वास्तव में बात नहीं है।
TheSmurf

55
@ जोश: किसी के लिए भी जो इसे पहले से ही नहीं जानता, बस यह कहना कि यह एक अपर्याप्त उत्तर है, क्योंकि यह काफी संभावना है कि वे नहीं जानते कि इसका क्या मतलब है, या तो।
TheSmurf

1
मैंने इसे केवल इसलिए अस्वीकृत कर दिया है क्योंकि मुझे लगता है कि अन्य उत्तरों में से एक शीर्ष पर होना चाहिए - कोई भी उत्तर जो कहता है कि "अनवांटेड कोड के साथ इंटरोप के लिए, अन्यथा बचें"।
डैनियल इयरविकर

59

"यह एक मान है" जवाब के अलावा, स्ट्रक्चर्स का उपयोग करने के लिए एक विशिष्ट परिदृश्य तब होता है जब आपको पता चलता है कि आपके पास डेटा का एक सेट है जो कचरा संग्रह के मुद्दों का कारण बन रहा है, और आपके पास बहुत सारी वस्तुएं हैं। उदाहरण के लिए, व्यक्तिगत उदाहरणों की एक बड़ी सूची / सरणी। यहां प्राकृतिक रूपक एक वर्ग है, लेकिन यदि आपके पास लंबे समय से व्यक्तिगत व्यक्ति उदाहरण हैं, तो वे GEN-2 को रोक सकते हैं और GC स्टालों का कारण बन सकते हैं। यदि परिदृश्य इसे वारंट करता है, तो यहां एक संभावित दृष्टिकोण व्यक्ति संरचना के एक सरणी (सूची नहीं) का उपयोग करना है , अर्थात Person[]। अब GEN-2 में लाखों ऑब्जेक्ट होने के बजाय, आपके पास LOH पर एक सिंगल चंक है (मैं यहां कोई स्ट्रिंग्स नहीं मान रहा हूं - यानी बिना किसी संदर्भ के शुद्ध मूल्य)। इसका GC प्रभाव बहुत कम है।

इस डेटा के साथ काम करना अजीब है, क्योंकि डेटा संभवतः एक संरचना के लिए अति-आकार है, और आप हर समय वसा मूल्यों को कॉपी नहीं करना चाहते हैं। हालांकि, इसे सीधे किसी ऐरे में एक्सेस करने से स्ट्रक्चर कॉपी नहीं होता है - यह इन-प्लेस (एक सूची इंडेक्स के विपरीत, जो कॉपी करता है)। इसका मतलब इंडेक्स के साथ बहुत सारे काम हैं:

int index = ...
int id = peopleArray[index].Id;

ध्यान दें कि मूल्यों को स्वयं अपरिवर्तनीय रखने से यहां मदद मिलेगी। अधिक जटिल तर्क के लिए, बाय-रेफ पैरामीटर के साथ एक विधि का उपयोग करें:

void Foo(ref Person person) {...}
...
Foo(ref peopleArray[index]);

फिर, यह जगह में है - हमने मूल्य की नकल नहीं की है।

बहुत विशिष्ट परिदृश्यों में, यह रणनीति बहुत सफल हो सकती है; हालाँकि, यह एक काफी उन्नत स्केलेरियो है जिसे केवल तभी जाना चाहिए जब आपको पता हो कि आप क्या कर रहे हैं और क्यों कर रहे हैं। यहां डिफ़ॉल्ट एक वर्ग होगा।


+1 दिलचस्प जवाब। क्या आप इस तरह के दृष्टिकोण के बारे में किसी वास्तविक दुनिया के उपाख्यानों को साझा करने के लिए तैयार होंगे?
जोर्डो

मोबाइल पर में @Jordao, लेकिन के लिए गूगल खोज: + Gravell + "जी सी द्वारा हमला"
मार्क Gravell

1
बहुत बहुत धन्यवाद। मैंने इसे यहां पाया ।
जोर्डो

2
@MarcGravell आपने क्यों उल्लेख किया: एक सरणी (सूची नहीं) का उपयोग करें ? Listमेरा मानना ​​है, एक Arrayपर्दे के पीछे का उपयोग करता है । नहीं ?
रॉय नमिर

4
@RoyiNamir मैं इसके बारे में उत्सुक था, लेकिन मेरा मानना ​​है कि उत्तर मार्क के उत्तर के दूसरे पैराग्राफ में निहित है। "हालांकि, इसे सीधे किसी ऐरे में एक्सेस करने से स्ट्रक्चर कॉपी नहीं होता है - यह इन-प्लेस (एक सूची इंडेक्सर के विपरीत है, जो कॉपी करता है)।"
user1323245 12

40

से सी # भाषा विनिर्देश :

1.7 संरचनाएं

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

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

class Point
{
   public int x, y;

   public Point(int x, int y) {
      this.x = x;
      this.y = y;
   }
}

class Test
{
   static void Main() {
      Point[] points = new Point[100];
      for (int i = 0; i < 100; i++) points[i] = new Point(i, i);
   }
}

बिंदु को एक संरचना बनाने के लिए एक विकल्प है।

struct Point
{
   public int x, y;

   public Point(int x, int y) {
      this.x = x;
      this.y = y;
   }
}

अब, केवल एक ऑब्जेक्ट को इंस्टेंट किया गया है - एक सरणी के लिए - और पॉइंट इंस्टेंस को सरणी में इन-लाइन में संग्रहीत किया जाता है।

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

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

Point a = new Point(10, 10);
Point b = a;
a.x = 20;
Console.WriteLine(b.x);

यदि प्वाइंट एक वर्ग है, तो आउटपुट 20 है क्योंकि ए और बी एक ही ऑब्जेक्ट को संदर्भित करते हैं। यदि बिंदु एक संरचना है, तो आउटपुट 10 है क्योंकि a से b का असाइनमेंट मान की एक प्रति बनाता है, और यह प्रतिलिपि अक्ष के बाद के असाइनमेंट से अप्रभावित है

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


4
हालांकि यह तथ्य कि संरचना के संदर्भों को बरकरार नहीं रखा जा सकता है, कभी-कभी एक सीमा होती है, यह एक बहुत ही उपयोगी विशेषता है। .Net की प्रमुख कमजोरियों में से एक यह है कि उस वस्तु पर हमेशा के लिए नियंत्रण खोए बिना एक उत्परिवर्तित वस्तु के संदर्भ में बाहर कोड पारित करने का कोई सभ्य तरीका नहीं है। इसके विपरीत, कोई सुरक्षित रूप refसे एक परिवर्तनशील संरचना को एक बाहरी विधि दे सकता है और यह जान सकता है कि कोई भी उत्परिवर्तन जो बाहरी विधि उस पर प्रदर्शन करेगा, लौटने से पहले किया जाएगा। यह बहुत बुरा है। नेट में
पंचांग

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

34

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


4
हां, लेकिन बड़े संदर्भ वर्ग संदर्भों से अधिक महंगे हो सकते हैं (जब तरीकों के आसपास से गुजर रहे हों)।
एलेक्स

27

यहाँ एक बुनियादी नियम है।

  • यदि सभी सदस्य फ़ील्ड मान प्रकार हैं, तो एक संरचना बनाएँ ।

  • यदि कोई एक सदस्य फ़ील्ड एक संदर्भ प्रकार है, तो एक वर्ग बनाएं । इसका कारण यह है कि संदर्भ प्रकार फ़ील्ड को वैसे भी ढेर आवंटन की आवश्यकता होगी।

exmaples

public struct MyPoint 
{
    public int X; // Value Type
    public int Y; // Value Type
}

public class MyPointWithName 
{
    public int X; // Value Type
    public int Y; // Value Type
    public string Name; // Reference Type
}

3
अपरिवर्तनीय संदर्भ प्रकार जैसे stringकि शब्दार्थ मूल्यों के समतुल्य हैं, और किसी क्षेत्र में अपरिवर्तनीय वस्तु के संदर्भ को संग्रहीत करने से ढेर आवंटन नहीं होता है। उजागर सार्वजनिक क्षेत्रों के साथ एक संरचना और उजागर सार्वजनिक क्षेत्रों के साथ एक वर्ग वस्तु के बीच का अंतर यह है कि कोड अनुक्रम दिया गया है var q=p; p.X=4; q.X=5;, p.Xका मान 4 होगा यदि aएक संरचना प्रकार है, और 5 यदि यह एक वर्ग प्रकार है। यदि कोई इस प्रकार के सदस्यों को सुविधाजनक रूप से संशोधित करने में सक्षम होना चाहता है, तो किसी qको प्रभावित करने के लिए परिवर्तनों के आधार पर 'वर्ग' या 'संरचना' का चयन करना चाहिए p
सुपरकैट

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

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

मैंने कहा कि यह एक मजबूत निर्णायक कारक नहीं है क्योंकि अक्सर जब आप एक वर्ग या संरचना बना रहे होते हैं तो आपको यकीन नहीं होता कि इसका उपयोग कैसे किया जाएगा। इसलिए आप इस बात पर ध्यान केंद्रित करते हैं कि कैसे चीजें डिजाइन के नजरिए से ज्यादा मायने रखती हैं। वैसे भी मैंने कभी भी .NET लाइब्रेरी के किसी एक स्थान पर नहीं देखा है जहाँ एक संरचना में एक संदर्भ चर होता है।
उस्मान जफर

1
संरचना प्रकार ArraySegment<T>एक अतिक्रमण करता है T[], जो हमेशा एक वर्ग प्रकार होता है। KeyValuePair<TKey,TValue>सामान्य प्रकार के रूप में संरचना प्रकार अक्सर क्लास प्रकार के साथ उपयोग किया जाता है।
सुपरकैट

19

पहला: इंटरॉप परिदृश्य या जब आपको मेमोरी लेआउट निर्दिष्ट करने की आवश्यकता होती है

दूसरा: जब डेटा वैसे भी लगभग एक संदर्भ सूचक के समान आकार का होता है।


17

आप एक "struct" स्थितियों में उपयोग करने के लिए जहां आप स्पष्ट रूप से का उपयोग कर स्मृति लेआउट निर्दिष्ट करना चाहते हैं की जरूरत है StructLayoutAttribute आम तौर पर PInvoke के लिए -।

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


5
स्ट्रक्चलाइयूटअट्रैक्शन को स्ट्रक्चर्स या क्लासेस पर लागू किया जा सकता है, इसलिए यह स्ट्रक्चर्स का उपयोग करने का कारण नहीं है।
स्टीफन मार्टिन

यदि आप किसी अनवांटेड मेथड कॉल के लिए एक तर्क दे रहे हैं तो यह क्यों समझ में आता है?
डेविड क्लेम्फनर

16

मैं किसी भी प्रकार के बाइनरी संचार प्रारूप को पैकिंग या अनपैक करने के लिए संरचनाओं का उपयोग करता हूं। जिसमें डिस्क पर पढ़ना या लिखना, डायरेक्टएक्स वर्टेक्स लिस्ट, नेटवर्क प्रोटोकॉल, या एन्क्रिप्टेड / कंप्रेस्ड डेटा से निपटना शामिल है।

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


क्या आप इतनी आसानी से उसके लिए एक संदर्भ प्रकार का उपयोग नहीं कर सकते?
डेविड क्लेम्फनर

15

PInvoke प्रयोजनों के लिए रनटाइम और विभिन्न अन्य लोगों द्वारा सीधे उपयोग किए जाने वाले वैल्यूएटाइप्स के अपवाद के साथ, आपको केवल 2 परिदृश्यों में वैल्यूसेटेप्स का उपयोग करना चाहिए।

  1. जब आपको कॉपी शब्दार्थों की आवश्यकता हो।
  2. जब आपको स्वचालित प्रारंभ की आवश्यकता होती है, आम तौर पर इन प्रकारों के सरणियों में।

# 2 .Net संग्रह कक्षाओं में संरचना के व्यापकता के कारण का हिस्सा लगता है ..
IAbstract

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

13

.NET समर्थन करता है value typesऔर reference types(जावा में, आप केवल संदर्भ प्रकारों को परिभाषित कर सकते हैं)। reference typesप्रबंधित हीप में आवंटित किए जाने के उदाहरण हैं और जब उनके पास कोई बकाया संदर्भ नहीं होता है तो कचरा एकत्र किया जाता है। value typesदूसरी ओर, के उदाहरणों को आवंटित किया जाता है stack, और इसलिए जैसे ही उनका दायरा समाप्त होता है, उन्हें आवंटित मेमोरी पुनः प्राप्त हो जाती है। और हां, value typesमूल्य और reference typesसंदर्भ द्वारा पारित हो । System.String को छोड़कर सभी C # आदिम डेटा प्रकार, मान प्रकार हैं।

कक्षा में संरचना का उपयोग करने के लिए,

C # में, structsहैं value types, वर्ग हैं reference types। आप enumकीवर्ड और कीवर्ड का उपयोग करके C # में मान प्रकार बना सकते हैं struct। वसीयत के value typeबजाय reference typeप्रबंधित ढेर पर कम वस्तुओं का उपयोग करना , जिसके परिणामस्वरूप कचरा कलेक्टर (जीसी) पर कम लोड होता है, कम लगातार जीसी चक्र, और परिणामस्वरूप बेहतर प्रदर्शन होता है। हालाँकि, value typesउनके डाउनसाइड भी हैं। structएक संदर्भ को पारित करने की तुलना में एक बड़े के आसपास गुजरना निश्चित रूप से महंगा है, यह एक स्पष्ट समस्या है। दूसरी समस्या ओवरहेड से जुड़ी है boxing/unboxing। यदि आप सोच रहे हैं कि क्या boxing/unboxingमतलब है, पर boxingऔर अच्छी व्याख्या के लिए इन लिंक का पालन करेंunboxing। प्रदर्शन के अलावा, ऐसे समय भी होते हैं जब आपको केवल प्रकार के शब्दार्थों की आवश्यकता होती है, जो कि यदि reference typesआपके पास है, तो इसे लागू करना बहुत मुश्किल (या बदसूरत) होगा। आपको value typesकेवल तभी उपयोग करना चाहिए , जब आपको सामान्य रूप arraysसे इन प्रकारों में प्रतिलिपि शब्दार्थों की आवश्यकता होती है या स्वचालित प्रारंभ की आवश्यकता होती है ।


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

... (2) जब विभिन्न कारणों से एक संरचना बस प्रयोग करने योग्य नहीं होगी [जैसे कि क्योंकि किसी को पेड़ की तरह कुछ के लिए नेस्टेड संदर्भ का उपयोग करने की आवश्यकता होती है, या क्योंकि किसी को बहुरूपता की आवश्यकता होती है]। ध्यान दें कि जब मूल्य प्रकारों का उपयोग किया जाता है, तो आम तौर पर किसी विशेष कारण से अनुपस्थित क्षेत्रों को सीधे उजागर करना चाहिए (जबकि अधिकांश वर्ग प्रकारों को गुणों के भीतर लपेटा जाना चाहिए)। संपत्तियों में फ़ील्ड के अनावश्यक आवरण से उत्परिवर्तित मूल्य प्रकारों के तथाकथित "बुराइयों" में से कई (उदाहरण के लिए, जबकि कुछ संकलक किसी को केवल-पढ़ने के लिए संरचना पर एक संपत्ति सेटर को कॉल करने की अनुमति देंगे क्योंकि यह कभी-कभी होगा ...
Supercat

... सही काम करते हैं, सभी संकलक ऐसी संरचनाओं पर सीधे फ़ील्ड सेट करने के प्रयासों को ठीक से अस्वीकार कर देंगे; कंपाइलर्स अस्वीकार को सुनिश्चित करने का सबसे अच्छा तरीका केवल पढ़ने के लिए संपत्ति readOnlyStruct.someMember = 5;बनाना नहीं है someMember, बल्कि इसे एक क्षेत्र बनाना है।
सुपरैट

12

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

public struct IntStruct {
    public int Value {get; set;}
}

5 उदाहरणों में निम्नलिखित परिणाम की सावधानीस्मृति में संग्रहीत संरचना के :

var struct1 = new IntStruct() { Value = 0 }; // original
var struct2 = struct1;  // A copy is made
var struct3 = struct2;  // A copy is made
var struct4 = struct3;  // A copy is made
var struct5 = struct4;  // A copy is made

// NOTE: A "copy" will occur when you pass a struct into a method parameter.
// To avoid the "copy", use the ref keyword.

// Although structs are designed to use less system resources
// than classes.  If used incorrectly, they could use significantly more.

एक वर्ग एक संदर्भ प्रकार है। जब आप एक वर्ग को एक नए चर में निर्दिष्ट करते हैं, तो चर में मूल वर्ग वस्तु का संदर्भ होता है।

public class IntClass {
    public int Value {get; set;}
}

स्मृति में वर्ग ऑब्जेक्ट के केवल एक उदाहरण में निम्न परिणाम ।

var class1 = new IntClass() { Value = 0 };
var class2 = class1;  // A reference is made to class1
var class3 = class2;  // A reference is made to class1
var class4 = class3;  // A reference is made to class1
var class5 = class4;  // A reference is made to class1  

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

var struct1 = new IntStruct() { Value = 0 };
var struct2 = struct1;
struct2.Value = 1;
// At this point, a developer may be surprised when 
// struct1.Value is 0 and not 1

12

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

तो निष्कर्ष यह है: LINQ और छिपी हुई संरचना बॉक्सिंग / अनबॉक्सिंग से सावधान रहें और माइक्रोप्टीमाइजेशन के लिए स्ट्रक्चर्स का उपयोग करके ऐरे के साथ सख्ती से रहें।

पुनश्च कॉल स्टैक के माध्यम से संरचना / कक्षा पास करने के बारे में एक और बेंचमार्क है https://stackoverflow.com/a/47864451/506147

BenchmarkDotNet=v0.10.8, OS=Windows 10 Redstone 2 (10.0.15063)
Processor=Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount=4
Frequency=3233542 Hz, Resolution=309.2584 ns, Timer=TSC
  [Host] : Clr 4.0.30319.42000, 64bit RyuJIT-v4.7.2101.1
  Clr    : Clr 4.0.30319.42000, 64bit RyuJIT-v4.7.2101.1
  Core   : .NET Core 4.6.25211.01, 64bit RyuJIT


          Method |  Job | Runtime |      Mean |     Error |    StdDev |       Min |       Max |    Median | Rank |  Gen 0 | Allocated |
---------------- |----- |-------- |----------:|----------:|----------:|----------:|----------:|----------:|-----:|-------:|----------:|
   TestListClass |  Clr |     Clr |  5.599 us | 0.0408 us | 0.0382 us |  5.561 us |  5.689 us |  5.583 us |    3 |      - |       0 B |
  TestArrayClass |  Clr |     Clr |  2.024 us | 0.0102 us | 0.0096 us |  2.011 us |  2.043 us |  2.022 us |    2 |      - |       0 B |
  TestListStruct |  Clr |     Clr |  8.427 us | 0.1983 us | 0.2204 us |  8.101 us |  9.007 us |  8.374 us |    5 |      - |       0 B |
 TestArrayStruct |  Clr |     Clr |  1.539 us | 0.0295 us | 0.0276 us |  1.502 us |  1.577 us |  1.537 us |    1 |      - |       0 B |
   TestLinqClass |  Clr |     Clr | 13.117 us | 0.1007 us | 0.0892 us | 13.007 us | 13.301 us | 13.089 us |    7 | 0.0153 |      80 B |
  TestLinqStruct |  Clr |     Clr | 28.676 us | 0.1837 us | 0.1534 us | 28.441 us | 28.957 us | 28.660 us |    9 |      - |      96 B |
   TestListClass | Core |    Core |  5.747 us | 0.1147 us | 0.1275 us |  5.567 us |  5.945 us |  5.756 us |    4 |      - |       0 B |
  TestArrayClass | Core |    Core |  2.023 us | 0.0299 us | 0.0279 us |  1.990 us |  2.069 us |  2.013 us |    2 |      - |       0 B |
  TestListStruct | Core |    Core |  8.753 us | 0.1659 us | 0.1910 us |  8.498 us |  9.110 us |  8.670 us |    6 |      - |       0 B |
 TestArrayStruct | Core |    Core |  1.552 us | 0.0307 us | 0.0377 us |  1.496 us |  1.618 us |  1.552 us |    1 |      - |       0 B |
   TestLinqClass | Core |    Core | 14.286 us | 0.2430 us | 0.2273 us | 13.956 us | 14.678 us | 14.313 us |    8 | 0.0153 |      72 B |
  TestLinqStruct | Core |    Core | 30.121 us | 0.5941 us | 0.5835 us | 28.928 us | 30.909 us | 30.153 us |   10 |      - |      88 B |

कोड:

[RankColumn, MinColumn, MaxColumn, StdDevColumn, MedianColumn]
    [ClrJob, CoreJob]
    [HtmlExporter, MarkdownExporter]
    [MemoryDiagnoser]
    public class BenchmarkRef
    {
        public class C1
        {
            public string Text1;
            public string Text2;
            public string Text3;
        }

        public struct S1
        {
            public string Text1;
            public string Text2;
            public string Text3;
        }

        List<C1> testListClass = new List<C1>();
        List<S1> testListStruct = new List<S1>();
        C1[] testArrayClass;
        S1[] testArrayStruct;
        public BenchmarkRef()
        {
            for(int i=0;i<1000;i++)
            {
                testListClass.Add(new C1  { Text1= i.ToString(), Text2=null, Text3= i.ToString() });
                testListStruct.Add(new S1 { Text1 = i.ToString(), Text2 = null, Text3 = i.ToString() });
            }
            testArrayClass = testListClass.ToArray();
            testArrayStruct = testListStruct.ToArray();
        }

        [Benchmark]
        public int TestListClass()
        {
            var x = 0;
            foreach(var i in testListClass)
            {
                x += i.Text1.Length + i.Text3.Length;
            }
            return x;
        }

        [Benchmark]
        public int TestArrayClass()
        {
            var x = 0;
            foreach (var i in testArrayClass)
            {
                x += i.Text1.Length + i.Text3.Length;
            }
            return x;
        }

        [Benchmark]
        public int TestListStruct()
        {
            var x = 0;
            foreach (var i in testListStruct)
            {
                x += i.Text1.Length + i.Text3.Length;
            }
            return x;
        }

        [Benchmark]
        public int TestArrayStruct()
        {
            var x = 0;
            foreach (var i in testArrayStruct)
            {
                x += i.Text1.Length + i.Text3.Length;
            }
            return x;
        }

        [Benchmark]
        public int TestLinqClass()
        {
            var x = testListClass.Select(i=> i.Text1.Length + i.Text3.Length).Sum();
            return x;
        }

        [Benchmark]
        public int TestLinqStruct()
        {
            var x = testListStruct.Select(i => i.Text1.Length + i.Text3.Length).Sum();
            return x;
        }
    }

क्या आपको पता लगा है कि सूचियों और इस तरह से उपयोग किए जाने पर स्ट्रक्चर्स इतने धीमे क्यों होते हैं? क्या यह छिपे हुए मुक्केबाजी और अनबॉक्सिंग के कारण है जिसका आपने उल्लेख किया है? यदि ऐसा है तो ऐसा क्यों होता है?
मार्को ग्रैडिक

किसी अतिरिक्त रेफरेंस की आवश्यकता न होने के कारण एरे में एक्सेसिंग स्ट्रक्चर जल्दी होना चाहिए। लाइनिंग के लिए बॉक्सिंग / अनबॉक्सिंग मामला है।
रोमन

10

C # या अन्य .net भाषाओं में संरचना प्रकार आमतौर पर उन चीजों को धारण करने के लिए उपयोग किया जाता है, जिन्हें मूल्यों के निश्चित-आकार वाले समूहों की तरह व्यवहार करना चाहिए। संरचना प्रकारों का एक उपयोगी पहलू यह है कि संरचना-प्रकार के उदाहरण के क्षेत्रों को भंडारण स्थान को संशोधित करके संशोधित किया जा सकता है जिसमें यह आयोजित होता है, और किसी अन्य तरीके से नहीं। इस तरह से एक संरचना को कोड करना संभव है कि किसी भी क्षेत्र को म्यूट करने का एकमात्र तरीका पूरी नई आवृत्ति का निर्माण करना है और फिर लक्ष्य के सभी क्षेत्रों को नए उदाहरण से मानों के साथ अधिलेखित करके एक संरचना असाइनमेंट का उपयोग करना है, लेकिन जब तक कोई संरचना ऐसा उदाहरण बनाने का कोई साधन उपलब्ध नहीं कराती है जहाँ उसके क्षेत्रों में गैर-डिफ़ॉल्ट मान हैं, तब तक उसके सभी फ़ील्ड परस्पर परिवर्तनशील होंगे यदि और यदि वह संरचना स्वयं एक परिवर्तनशील स्थान पर संग्रहीत है।

ध्यान दें कि एक संरचना प्रकार डिजाइन करना संभव है ताकि यह अनिवार्य रूप से एक वर्ग प्रकार की तरह व्यवहार करे, यदि संरचना में एक निजी वर्ग-प्रकार का क्षेत्र होता है, और अपने स्वयं के सदस्यों को लिपटे हुए क्लास ऑब्जेक्ट के लिए पुनर्निर्देशित करता है। उदाहरण के लिए, एक PersonCollectionगुण की पेशकश कर सकते हैं SortedByNameऔर SortedById, दोनों एक PersonCollection(उनके निर्माणकर्ता में सेट) के लिए एक "अपरिवर्तनीय" संदर्भ रखते हैं और या GetEnumeratorतो कॉल करके लागू करते हैं । इस तरह की संरचनाएं एक संदर्भ के समान व्यवहार करती हैं , सिवाय इसके कि उनके तरीके अलग-अलग तरीकों से बंधे होंगे । एक भी एक सरणी के एक हिस्से को लपेट कर एक संरचना हो सकती है (जैसे कोई एक संरचना को परिभाषित कर सकता है जो एक बुलाया , एक इंट , और एक इंट धारण करेगाcreator.GetNameSortedEnumeratorcreator.GetIdSortedEnumeratorPersonCollectionGetEnumeratorPersonCollectionArrayRange<T>T[]ArrOffsetLength, एक अनुक्रमित संपत्ति के साथ, जो idxकि 0 से सीमा में एक सूचकांक के लिए Length-1उपयोग करेगा Arr[idx+Offset])। दुर्भाग्य से, अगर fooइस तरह की संरचना का केवल-पढ़ने का उदाहरण है, तो वर्तमान संकलक संस्करण संचालन की अनुमति नहीं देंगे foo[3]+=4;क्योंकि उनके पास यह निर्धारित करने का कोई तरीका नहीं है कि क्या इस तरह के संचालन के क्षेत्रों में लिखने का प्रयास किया जाएगा foo

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


10

नाह - मैं पूरी तरह से नियमों से सहमत नहीं हूं। वे प्रदर्शन और मानकीकरण के साथ विचार करने के लिए अच्छे दिशानिर्देश हैं, लेकिन संभावनाओं के प्रकाश में नहीं।

जैसा कि आप प्रतिक्रियाओं में देख सकते हैं, उनका उपयोग करने के लिए बहुत सारे रचनात्मक तरीके हैं। इसलिए, इन दिशानिर्देशों का प्रदर्शन और कार्यकुशलता के लिए सिर्फ इतना ही होना चाहिए।

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

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

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


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

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

8

मेरा नियम है

1, हमेशा कक्षा का उपयोग करें;

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


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

1
@ सुपरकैट: मुझे लगता है कि इसके लिए Microsoft को दोष देना पूरी तरह से उचित नहीं है। यहाँ वास्तविक मुद्दा यह है कि C # एक वस्तु-उन्मुख भाषा के रूप में केवल सादे रिकॉर्ड प्रकारों पर ध्यान केंद्रित नहीं करता है जो केवल बिना अधिक व्यवहार के डेटा को उजागर करते हैं। C # उस सीमा तक बहु-प्रतिमान भाषा नहीं है, जैसे C ++ है। यही कारण है कि किया जा रहा है कहा, मैं भी मानना है कि बहुत कम लोगों को शुद्ध OOP कार्यक्रम है, तो शायद सी # भी आदर्शवादी एक भाषा है। (मैं एक के लिए हाल ही public readonlyमें अपने प्रकारों में फ़ील्ड्स को उजागर करना शुरू कर चुका हूं, भी, क्योंकि रीड-ओनली प्रॉपर्टीज़ बनाने से आमतौर पर बिना किसी लाभ के बहुत अधिक काम होता है।)
स्टैक्क्स - अब

1
@stakx: इस प्रकार के "फोकस" के लिए इसकी कोई आवश्यकता नहीं है; जो कुछ वे करेंगे उसके लिए उन्हें पहचानना। सी # के साथ सबसे बड़ी कमजोरी संरचना के संबंध में कई अन्य क्षेत्रों में भी इसकी सबसे बड़ी समस्या है: भाषा यह संकेत देने के लिए अपर्याप्त सुविधाएं प्रदान करती है कि कुछ परिवर्तन कब उचित हैं या नहीं हैं, और इस तरह की सुविधाओं की कमी दुर्भाग्यपूर्ण डिजाइन निर्णय लेती है। उदाहरण के लिए, की "अस्थायी structs बुराई कर रहे हैं" 99% मोड़ संकलक की वजह से उपजी MyListOfPoint[3].Offset(2,3);में var temp=MyListOfPoint[3]; temp.Offset(2,3);, एक को बदलने जो फर्जी है जब लागू किया ...
supercat

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

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

8

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

कक्षाएं और संरचनाएं (C # प्रोग्रामिंग गाइड)


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

5

मुझे लगता है कि एक अच्छा पहला सन्निकटन "कभी नहीं" है।

मुझे लगता है कि एक अच्छा दूसरा सन्निकटन "कभी नहीं" है।

यदि आप सिद्ध के लिए बेताब हैं, तो उन पर विचार करें, लेकिन फिर हमेशा माप करें।


24
मैं उस जवाब से असहमत होगा। कई परिदृश्यों में संरचनाओं का एक वैध उपयोग होता है। यहाँ एक उदाहरण है - परमाणु तरीके से डेटा क्रॉस प्रक्रियाओं को मार्श करना।
फ्रैंकी पेनोव

25
आपको अपनी पोस्ट को संपादित करना चाहिए और अपने बिंदुओं पर विस्तार से बताना चाहिए - आपने अपनी राय दे दी है, लेकिन आपको यह वापस लेना चाहिए कि आप यह राय क्यों लेते हैं।
एरिक फोर्ब्स

4
मुझे लगता है कि उन्हें स्ट्रक्चर्स का उपयोग करने के लिए टोटिन चिप कार्ड ( en.wikipedia.org/wiki/Totin%27_Chip ) के बराबर की आवश्यकता है । गंभीरता से।
ग्रेग

4
87.5K व्यक्ति इस तरह से उत्तर कैसे देता है? क्या बच्चा होने पर उसने ऐसा किया था?
रोहित विपिन मैथ्यूज

3
@ रोहित - यह छह साल पहले था; साइट मानक तब बहुत अलग थे। यह अभी भी एक बुरा जवाब है, हालांकि, आप सही हैं।
एंड्रयू अर्नोल्ड

5

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


1
यह सभी का सबसे अच्छा सुराग है, IMHO।
इवान

5

मिथक # 1: ताकतें प्रकाशमान वर्ग हैं

यह मिथक कई रूपों में सामने आता है। कुछ लोगों का मानना ​​है कि मूल्य प्रकारों में तरीकों या अन्य महत्वपूर्ण व्यवहार नहीं हो सकते हैं या नहीं होने चाहिए - उन्हें केवल सार्वजनिक क्षेत्र या साधारण गुणों के साथ सरल डेटा स्थानांतरण प्रकार के रूप में उपयोग किया जाना चाहिए। डेटटाइम प्रकार इस के लिए एक अच्छा प्रतिरूप है: यह एक संख्या या वर्ण जैसी मौलिक इकाई होने के संदर्भ में एक मूल्य प्रकार होने के लिए समझ में आता है, और इसके आधार पर गणना करने में सक्षम होने के लिए भी यह समझ में आता है। इसका मूल्य। अन्य दिशा से चीजों को देखते हुए, डेटा ट्रांसफर प्रकार अक्सर संदर्भ प्रकार होने चाहिए - निर्णय वांछित मूल्य या संदर्भ प्रकार शब्दार्थ पर आधारित होना चाहिए, न कि प्रकार की सादगी। अन्य लोगों का मानना ​​है कि प्रदर्शन के संदर्भ में मूल्य प्रकार संदर्भ प्रकार की तुलना में "हल्का" हैं। सच्चाई यह है कि कुछ मामलों में मूल्य प्रकार अधिक प्रदर्शन करने वाले होते हैं- जब तक वे बॉक्सिंग नहीं करते, तब तक उन्हें कचरा संग्रहण की आवश्यकता नहीं होती है, उनके पास प्रकार के ओवरहेड नहीं होते हैं, और उदाहरण के लिए, डेरेफ़रिंग की आवश्यकता नहीं होती है। लेकिन अन्य तरीकों से, संदर्भ प्रकार अधिक निष्पादन योग्य हैं - पैरामीटर पासिंग, वैरिएबल को मान असाइन करना, मान वापस करना, और इसी तरह के संचालन के लिए केवल 4 या 8 बाइट्स की आवश्यकता होती है जिसे फिर से परिभाषित किया जाता है (इस पर निर्भर करते हुए कि आप 32-बिट या 64-बिट सीएलआर चला रहे हैं) ) सभी डेटा को कॉपी करने के बजाय। कल्पना कीजिए कि अगर ArrayList किसी तरह से "शुद्ध" मूल्य प्रकार का था, और एक ArrayList अभिव्यक्ति को अपने सभी डेटा को कॉपी करने वाली विधि से गुजर रहा है! लगभग सभी मामलों में, प्रदर्शन वास्तव में वैसे भी इस तरह के निर्णय से निर्धारित नहीं होता है। अड़चनें लगभग कभी नहीं होती हैं जहाँ आप सोचते हैं कि वे होंगे, और इससे पहले कि आप प्रदर्शन के आधार पर एक डिजाइन निर्णय लें, आपको विभिन्न विकल्पों को मापना चाहिए। यह ध्यान देने योग्य है कि दोनों मान्यताओं का संयोजन या तो काम नहीं करता है। इससे कोई फ़र्क नहीं पड़ता है कि एक प्रकार की कितनी विधियाँ हैं (चाहे वह एक वर्ग या एक संरचना है) - प्रति उदाहरण ली गई स्मृति प्रभावित नहीं होती है। (कोड के लिए ली गई मेमोरी के संदर्भ में एक लागत है, लेकिन यह प्रत्येक उदाहरण के बजाय एक बार हुआ है।)

मिथक # 2: संदर्भ के आधार पर रहते हैं; मूल्य के प्रकार स्टैक पर रहते हैं

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

मिथक # 3: OBJECTS को C # BY DEFAULT में संदर्भ द्वारा प्रस्तुत किया गया है

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

void AppendHello(StringBuilder builder)
{
    builder.Append("hello");
}

जब इस पद्धति को कहा जाता है, तो पैरामीटर मान (एक स्ट्रिंगबर्ल के लिए एक संदर्भ) मूल्य द्वारा पारित किया जाता है। यदि आप विधि के भीतर बिल्डर वैरिएबल के मान को बदलना चाहते हैं - उदाहरण के लिए, स्टेटमेंट बिल्डर = null के साथ; -इस बदलाव को कॉल करने वाले द्वारा मिथक के विपरीत नहीं देखा जाएगा। यह ध्यान रखना दिलचस्प है कि न केवल मिथक के "संदर्भ द्वारा" बिट गलत है, बल्कि इसलिए "ऑब्जेक्ट" को भी पारित कर दिया गया है। वस्तुओं को स्वयं कभी भी पारित नहीं किया जाता है, या तो संदर्भ या मूल्य से। जब एक संदर्भ प्रकार शामिल होता है, तो या तो चर को संदर्भ द्वारा पारित किया जाता है या तर्क के मान (संदर्भ) को मूल्य से पारित किया जाता है। किसी और चीज के अलावा, यह इस सवाल का जवाब देता है कि क्या होता है जब अशक्त को एक मान मूल्य तर्क के रूप में उपयोग किया जाता है - यदि ऑब्जेक्ट्स को पास किया जा रहा था, तो यह मुद्दों का कारण होगा, क्योंकि पास होने के लिए कोई ऑब्जेक्ट नहीं होगा! बजाय, अशक्त संदर्भ उसी तरह से मूल्य द्वारा पारित किया जाता है जैसे कोई अन्य संदर्भ होगा। यदि इस त्वरित व्याख्या ने आपको हतप्रभ कर दिया है, तो आप मेरे लेख, "C # में पारित होने वाले पैरामीटर", (http://mng.bz/otVt ), जो बहुत अधिक विस्तार में जाता है। ये मिथक आसपास के ही नहीं हैं। बॉक्सिंग और अनबॉक्सिंग गलतफहमी के अपने उचित हिस्से के लिए आते हैं, जिसे मैं आगे स्पष्ट करने की कोशिश करूंगा।

संदर्भ: जॉन स्केट द्वारा गहराई # 3 संस्करण में सी #


1
बहुत अच्छा मानकर आप सही हैं। संदर्भ जोड़ने के लिए भी बहुत अच्छा है।
NoChance

4

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

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


3

कचरा संग्रहण प्रदर्शन में सुधार के लिए संरचना का उपयोग किया जा सकता है। जब आपको आमतौर पर जीसी प्रदर्शन के बारे में चिंता करने की ज़रूरत नहीं होती है, तो ऐसे परिदृश्य होते हैं जहां यह हत्यारा हो सकता है। कम विलंबता अनुप्रयोगों में बड़े कैश की तरह। इस पोस्ट को एक उदाहरण के लिए देखें:

http://00sharp.wordpress.com/2013/07/03/a-case-for-the-struct/


3

संरचना या मूल्य प्रकार निम्न परिदृश्यों में उपयोग किए जा सकते हैं -

  1. यदि आप कचरा संग्रहण द्वारा एकत्रित की जाने वाली वस्तु को रोकना चाहते हैं।
  2. यदि यह एक सरल प्रकार है और कोई सदस्य फ़ंक्शन इसके उदाहरण फ़ील्ड को संशोधित नहीं करता है
  3. यदि अन्य प्रकारों से प्राप्त करने या अन्य प्रकारों से व्युत्पन्न होने की आवश्यकता नहीं है।

आप इस लिंक पर मूल्य प्रकार और मान प्रकार के बारे में अधिक जान सकते हैं


3

संक्षेप में, यदि संरचना का उपयोग करें:

1- आपकी ऑब्जेक्ट प्रॉपर्टीज / फील्ड्स को बदलने की जरूरत नहीं है। मेरा मतलब है कि आप उन्हें शुरुआती मूल्य देना चाहते हैं और फिर उन्हें पढ़ सकते हैं।

2- आपकी वस्तु में गुण और क्षेत्र मूल्य प्रकार हैं और वे इतने बड़े नहीं हैं।

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


2

मैं शायद ही कभी चीजों के लिए एक संरचना का उपयोग करता हूं। लेकिन वह सिर्फ मैं हूं। यह निर्भर करता है कि मुझे वस्तु को अशक्त होने की आवश्यकता है या नहीं।

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


-11

संरचनाएं अधिकांश तरीकों से होती हैं जैसे कक्षाएं / वस्तुएं। संरचना में कार्य, सदस्य हो सकते हैं और विरासत में मिल सकते हैं। लेकिन संरचनाएं C # डेटा होल्डिंग के लिए उपयोग की जाती हैं । संरचनाएं कक्षाओं की तुलना में कम रैम लेती हैं और कचरा संग्रहकर्ता के लिए इकट्ठा करना आसान होता है । लेकिन जब आप अपनी संरचना में कार्यों का उपयोग करते हैं, तो संकलक वास्तव में उस संरचना को वर्ग / वस्तु के समान लेता है, इसलिए यदि आप कार्यों के साथ कुछ चाहते हैं , तो कक्षा / वस्तु का उपयोग करें


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