2009-12-30 19 views
16

सी # में, किसी भी उपयोगकर्ता परिभाषित struct स्वचालित रूप से System.StructSystem.ValueType और System.Struct System.ValueTypeSystem.Object का एक उपवर्ग है का एक उपवर्ग है। structs को बॉक्स करने की आवश्यकता क्यों है?

लेकिन जब हम वस्तु-प्रकार संदर्भ के लिए कुछ संरचना निर्दिष्ट करते हैं तो यह बॉक्सिंग हो जाता है। उदाहरण के लिए:

struct A 
{ 
    public int i; 
} 

A a; 
object obj = a; // boxing takes place here 

तो मेरे सवाल है: यदि ASystem.Object के एक वंशज है, यह डाली अप नहीं कर सकते संकलक प्रकार के बजाय मुक्केबाजी आपत्ति के लिए?

उत्तर

35

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

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

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

+4

+1 आपको +1 क्योंकि आप इसे मुझसे बेहतर समझते हैं .. –

+1

धन्यवाद मेहदद! इससे मेरा संदेह साफ हो गया! :) –

+5

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

0

struct डिज़ाइन द्वारा मूल्य-प्रकार है, इसलिए संदर्भ प्रकार में बदलते समय इसे बॉक्स किया जाना चाहिए। structSystem.ValueType से निकला है, जो शब्द System.Object से प्राप्त होता है।

मात्र तथ्य यह है कि struct वस्तु का एक वंशज है, अलग तरह से एक संदर्भ प्रकार की तुलना में कार्यावधि में structs साथ much..since CLR सौदों मतलब यह नहीं है।

3

नेट के डिजाइनर निश्चित रूप से मुक्केबाजी खंड शामिल करने के लिए C# Language Specification का 4.3, काफी अच्छी तरह से इसके पीछे मंशा बताते IMO जरूरत नहीं थी जबकि:

मुक्केबाजी और unboxing प्रकार का एक एकीकृत देखने में सक्षम बनाता है सिस्टम जिसमें किसी भी प्रकार का मान अंततः ऑब्जेक्ट के रूप में माना जा सकता है।

क्योंकि मान प्रकार संदर्भ प्रकार नहीं हैं (कौन सी प्रणाली।ऑब्जेक्ट अंततः है), एक एकीकृत प्रकार प्रणाली रखने के लिए मुक्केबाजी का कार्य मौजूद है जहां का मूल्य किसी ऑब्जेक्ट के रूप में प्रदर्शित किया जा सकता है।

यह कहने से अलग है, सी ++ जहां टाइप सिस्टम एकीकृत नहीं है, सभी प्रकार के लिए एक सामान्य आधार प्रकार नहीं है।

+1

कड़ाई से बोलते हुए, ऑब्जेक्ट से सबकुछ नहीं निकलता है। प्रकार की तरफ, पॉइंटर प्रकार न तो परिवर्तनीय होते हैं और न ही ऑब्जेक्ट से व्युत्पन्न होते हैं। प्रकार पैरामीटर प्रकार और इंटरफ़ेस प्रकार ऑब्जेक्ट से प्राप्त नहीं होते हैं लेकिन ऑब्जेक्ट में हमेशा परिवर्तनीय होते हैं। यह गैर-सूचक प्रकारों के * मान * है जो हमेशा ऑब्जेक्ट से प्राप्त होते हैं। संदर्भ प्रकारों के शून्य मान को छोड़कर, जो किसी वस्तु से नहीं, किसी वस्तु से नहीं निकलता है। एक संदर्भ जो कुछ भी संदर्भित करता है, किसी वस्तु का संदर्भ नहीं देता है; ऐसा संदर्भ ऑब्जेक्ट में परिवर्तनीय है लेकिन ऑब्जेक्ट से प्राप्त नहीं होता है। –

+0

@ एरिक लिपर्ट: आपकी चिंताओं को दर्शाने के लिए उत्तर बदल दिया। – casperOne

+0

सी # विनिर्देश के अनुसार, सभी मान प्रकार संदर्भ प्रकार ('System.ValueType' और 'System.Enum') से व्युत्पन्न होते हैं, लेकिन संदर्भ प्रकार नहीं हैं। जो बकवास है और मुझे संदेह है कि सी # विनिर्देश गलत है। @EricLippert: मुझे आपकी राय [मेरे उत्तर] में रुचि होगी (http://stackoverflow.com/a/15031902/240733)। – stakx

15

यहां बताया गया है कि मैं इसके बारे में क्या सोचना पसंद करता हूं। एक 32 बिट पूर्णांक युक्त एक चर के कार्यान्वयन पर विचार करें। जब किसी मान प्रकार के रूप में माना जाता है, तो संपूर्ण मान 32 बिट स्टोरेज में फिट बैठता है। वैल्यू टाइप यह है कि: स्टोरेज में केवल बिट्स होते हैं जो मूल्य बनाते हैं, और कुछ भी नहीं, कम कुछ भी नहीं।

अब किसी ऑब्जेक्ट संदर्भ वाले चर के कार्यान्वयन पर विचार करें। चर में एक "संदर्भ" होता है, जिसे किसी भी तरीके से कार्यान्वित किया जा सकता है। यह एक कचरा कलेक्टर संरचना में एक संभाल हो सकता है, या यह प्रबंधित ढेर, या जो भी हो, पर एक पता हो सकता है। लेकिन यह ऐसा कुछ है जो आपको ऑब्जेक्ट खोजने की अनुमति देता है। संदर्भ संदर्भ यह है कि: संदर्भ प्रकार के चर के साथ जुड़े भंडारण में कुछ बिट्स हैं जो आपको किसी ऑब्जेक्ट को संदर्भित करने की अनुमति देती हैं।

स्पष्ट रूप से उन दो चीजें पूरी तरह से अलग हैं।

अब मान लीजिए कि आपके पास प्रकार ऑब्जेक्ट का एक चर है, और आप उस प्रकार के चर के प्रकार की प्रतिलिपि बनाना चाहते हैं। आप इसे कैसे करते हो? 32 बिट्स जो एक पूर्णांक बनाते हैं वे इन "संदर्भ" चीजों में से एक नहीं हैं, यह केवल एक बाल्टी है जिसमें 32 बिट्स हैं। संदर्भ प्रबंधित ढेर में 64 बिट पॉइंटर्स हो सकते हैं, या कचरा कलेक्टर डेटा संरचना में 32 बिट हैंडल हो सकते हैं, या किसी अन्य कार्यान्वयन के बारे में आप सोच सकते हैं, लेकिन 32 बिट पूर्णांक केवल 32 बिट पूर्णांक हो सकता है।

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

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

+0

एरिक, हमेशा के रूप में, बहुत अच्छी व्याख्या! यद्यपि आप कृपया कह सकते हैं कि यदि आप एक एकीकृत प्रकार प्रणाली चाहते हैं तो मुक्केबाजी आवश्यक है, तो आप इसका मतलब क्या कर सकते हैं। मैं समझने में असफल रहा हूं कि कैसे 'मुक्केबाजी' प्रकार प्रणाली को एकीकृत करता है। धन्यवाद! – SolutionYogi

+0

इसके बारे में कुछ और सोचते हुए, क्या आप सुझाव दे रहे हैं कि सी # एक ऐसी प्रणाली पसंद करता है जहां डेवलपर संदर्भ प्रकार के समान मान प्रकारों का 'इलाज' कर सकता है, यह समझने की आवश्यकता के बिना कि वास्तव में उन्हें .NET CLR द्वारा कैसे कार्यान्वित किया जाता है? और इसे प्राप्त करने के लिए, 'मुक्केबाजी' आवश्यक बुराई हो जाता है? ऐसा लगता है कि अगर आपने 'मुक्केबाजी' से बचने का फैसला किया है, तो डेवलपर मूल्य प्रकार/संदर्भ प्रकारों के साथ कैसे सहभागिता करेगा? – SolutionYogi

+1

मुझे दोबारा दोहराएं। तीन वांछनीय चीजें: (1) मूल्य प्रकारों में केवल उनके डेटा होते हैं और इसलिए संदर्भ प्रकारों से भिन्न प्रतिनिधित्व होता है, (2) सभी मानों को एक सामान्य एकीकृत प्रकार, ऑब्जेक्ट में परिवर्तित किया जा सकता है, और (3) मूल्य प्रकारों को कभी भी " बॉक्सिंग "। उन तीन वांछनीय चीजें पारस्परिक रूप से असंभव हैं; आप उनमें से अधिकतर में हो सकते हैं। हमने (1) और (2) चुना है; नहीं (3) वह कीमत है जो आप भुगतान करते हैं। –

0

के बाद सवाल का जवाब दे दिया गया है मैं एक छोटे से "चाल" उस विषय से संबंधित पेश करेंगे:

struct रों इंटरफेस को लागू कर सकते हैं। यदि आप किसी फ़ंक्शन में मान प्रकार पास करते हैं जो एक इंटरफ़ेस की अपेक्षा करता है कि यह मान प्रकार लागू करता है तो सामान्य रूप से बॉक्स को प्राप्त किया जाता है। जेनरिक का उपयोग करके आप मुक्केबाजी से बच सकते हैं: "अगर struct ASystem.Object के एक वंशज है, यह मुक्केबाजी के बजाय डाली ऊपर संकलक नहीं कर सकते"

interface IFoo {...} 
struct Bar : IFoo {...} 

void boxing(IFoo x) { ... } 
void byValue<T>(T x) : where T : IFoo { ... } 

var bar = new Bar(); 
boxing(bar); 
byValue(bar); 
0

नहीं, बस क्योंकि सी # भाषा की परिभाषा के अनुसार, "ऊपर से कास्टिंग" इस मामले में मुक्केबाजी है।

सी # के लिए भाषा विनिर्देशन (अध्याय 13 में) सभी संभावित प्रकार रूपांतरणों की सूची है। इन सभी रूपांतरणों को एक विशिष्ट फैशन में वर्गीकृत किया गया है (उदा। संख्यात्मक रूपांतरण, संदर्भ रूपांतरण आदि)।

  1. इसके सुपर-प्रकार T करने के लिए एक प्रकार S से निहित प्रकार रूपांतरण कर रहे हैं, लेकिन ये केवल पैटर्न "एक वर्ग प्रकार S से एक संदर्भ प्रकार T करने के लिए" के लिए परिभाषित कर रहे हैं। चूंकि आपका struct A कक्षा प्रकार नहीं है, इसलिए इन रूपांतरणों को आपके उदाहरण में लागू नहीं किया जा सकता है।

    यह तथ्य है कि Aobject (अप्रत्यक्ष रूप से) से प्राप्त (अप्रत्यक्ष रूप से) यहां केवल अप्रासंगिक है। प्रासंगिक क्या है कि A एक संरचना मूल्य प्रकार है।

  2. केवल मौजूदा रूपांतरण कि "अपने संदर्भ सुपर-प्रकार object को एक मान प्रकार A से" पैटर्न से मेल खाता है एक बॉक्सिंग रूपांतरण के रूप में वर्गीकृत किया गया है। इस प्रकार struct से object से प्रत्येक रूपांतरण परिभाषा बॉक्सिंग माना जाता है।

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