2010-10-04 12 views
13

सी # में, अगर आपके पास एक struct तो जैसे:सी #: रीडोनली structs पर उत्परिवर्तन क्यों नहीं टूटते हैं?

struct Counter 
{ 
    private int _count; 

    public int Value 
    { 
     get { return _count; } 
    } 

    public int Increment() 
    { 
     return ++_count; 
    } 
} 

और आप तो जैसे किसी प्रोग्राम:

static readonly Counter counter = new Counter(); 

static void Main() 
{ 
    // print the new value from the increment function 
    Console.WriteLine(counter.Increment()); 
    // print off the value stored in the item 
    Console.WriteLine(counter.Value); 
} 

कार्यक्रम का उत्पादन होगा:

1 
0 

यह पूरी तरह गलत लगता है। मैं या तो आउटपुट को दो 1s होने की उम्मीद करता हूं (जैसा कि Counterclass है या struct Counter : ICounter और counterICounter है) या संकलन त्रुटि हो। मुझे एहसास है कि संकलन समय पर इसका पता लगाना एक कठिन मामला है, लेकिन यह व्यवहार तर्क का उल्लंघन करता प्रतीत होता है।

क्या इस व्यवहार के कार्यान्वयन में कठिनाई से परे कोई कारण है?

+10

एरिक लिपर्ट की ["उत्परिवर्ती रीडोनली स्ट्रक्चर"] देखें (http://blogs.msdn.com/b/ericlippert/archive/2008/05/14/mutating-readonly-structs.aspx) –

+0

* क्यों * दो 1 एस की उम्मीद है? आपने कहा था कि आप इसे पढ़ना चाहते हैं, तो आप इसे क्यों बदलना चाहते हैं? –

उत्तर

8

structs मूल्य प्रकार हैं और इसलिए एक मूल्य प्रकार sematics है। इसका मतलब यह है कि हर बार जब आप उस संरचना तक पहुंचते हैं जो आप मूल रूप से संरचना के मूल्य की एक प्रति के साथ काम करते हैं।

अपने नमूने में आप मूल struct नहीं बदलते हैं लेकिन इसकी एक अस्थायी प्रतिलिपि भी बदलते हैं।

आगे स्पष्टीकरण के लिए यहाँ देखें:

Why are mutable structs evil

+4

लेकिन यदि आप 'रीडोनली' लेते हैं, तो दो 1s की अपेक्षित आउटपुट होती है। तो यह स्पष्ट रूप से उस उदाहरण में एक प्रतिलिपि नहीं बनाता है। क्या इसका मतलब यह है कि रीडोनली structs पर बुलाए गए सभी कार्यों को संरचना की प्रतियां बनाते हैं (क्योंकि सी # में उत्परिवर्तनीय फ़ंक्शन की कोई अवधारणा नहीं है)? –

+4

हां, यह सी # भाषा विनिर्देश के खंड 7.5.4 के अनुसार मामला है। अधिक जानकारी प्राप्त करने के लिए विषय पर एरिक लिपर्ट की पोस्ट देखें। –

+1

.NET में एक दुर्भाग्यपूर्ण डिजाइन कमजोरी यह है कि कोई माध्यम नहीं है जिसके द्वारा संरचना विधियां इंगित कर सकती हैं कि वे 'इस' को संशोधित करेंगे या नहीं। चूंकि यह केवल परेशान होगा यदि कोई केवल पढ़ने-योग्य संरचनाओं पर किसी भी विधि या गुणों का उपयोग नहीं कर सका, और यह भी परेशान होगा यदि structs पर 'readonly' modifiers सम्मानित नहीं किया गया था, सी # और vb.net इसके बजाय "समझौता" केवल-पढ़ने वाली संरचनाओं पर विधि आमंत्रण होने से विधि को आविष्कार करने से पहले संरचना की एक प्रति बनाते हैं। ध्यान दें कि यदि कोई सीधे संरचना फ़ील्ड का खुलासा करता है, तो संकलक लिखने से पढ़ने की पहुंच को अलग करने में सक्षम होगा, और ... – supercat

3

.net में, एक struct उदाहरण विधि शब्दार्थ struct के एक अतिरिक्त ref पैरामीटर के साथ एक स्थिर struct विधि के बराबर है प्रकार।

struct Blah { 
    public int value; 
    public void Add(int Amount) { value += Amount; } 
    public static void Add(ref Blah it; int Amount; it.value += Amount;} 
} 

प्रणाली को बुलाती है:

someBlah.Add(5); 
Blah.Add(ref someBlah, 5); 

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

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

.NET में मेरे प्रमुख peeves में से एक यह है कि अभी भी (कम से कम 4.0, और शायद 4.5 के रूप में) अभी भी कोई विशेषता नहीं है जिसके माध्यम से एक स्ट्रक्चर सदस्य फ़ंक्शन इंगित कर सकता है कि यह this संशोधित करता है या नहीं।लोग रेलवे को अस्थायी रूप से उत्परिवर्तनीय तरीकों की पेशकश करने की अनुमति देने के लिए टूल प्रदान करने के बजाय, कैसे प्रदान किए जाने के बजाय structs अपरिवर्तनीय होना चाहिए। यह, इस तथ्य के बावजूद कि तथाकथित "अपरिवर्तनीय" structs एक झूठ हैं। म्यूटेबल स्टोरेज स्थानों में सभी गैर-तुच्छ मान प्रकार म्यूटेबल हैं, जैसा कि सभी बॉक्स किए गए मान प्रकार हैं। एक संरचना "अपरिवर्तनीय" बनाना एक संपूर्ण संरचना को फिर से लिखने के लिए मजबूर कर सकता है जब कोई केवल एक फ़ील्ड बदलना चाहता है, लेकिन struct1 = struct2 स्ट्रक्चर 2 से सभी सार्वजनिक और निजी फ़ील्ड की प्रतिलिपि बनाकर संरचना 1 को परिवर्तित करता है, और संरचना के लिए प्रकार की परिभाषा कुछ भी नहीं है इसे रोकने के लिए कर सकते हैं (किसी भी फ़ील्ड को छोड़कर) यह संरचना सदस्यों के अप्रत्याशित उत्परिवर्तन को रोकने के लिए कुछ भी नहीं करता है। इसके अलावा, थ्रेडिंग मुद्दों के कारण, structs उनके क्षेत्रों के बीच किसी प्रकार के invariant संबंध को लागू करने की उनकी क्षमता में बहुत सीमित हैं। आईएमएचओ, यह आम तौर पर मनमाने ढंग से क्षेत्र की पहुंच की अनुमति देने के लिए एक संरचना के लिए बेहतर होगा, यह स्पष्ट कर देगा कि किसी भी कोड को एक स्ट्रक्चर प्राप्त करने के लिए यह जांच करनी चाहिए कि क्या उसके क्षेत्र सभी आवश्यक स्थितियों को पूरा करते हैं, जो कि उन शर्तों के निर्माण को रोकने की कोशिश करते हैं, जो शर्तों को पूरा नहीं करते हैं।

+0

* '" struct1 = struct2' प्रतिलिपि बनाकर संरचना 1 को बदलता है "* - यह सच है, लेकिन क्या इसके साथ कोई व्यावहारिक समस्याएं हैं (सिवाय इसके कि यह आम तौर पर परमाणु नहीं है)? 'Struct1' और' struct2' दोनों वास्तविक डेटा से भरे चर या फ़ील्ड हैं, इसलिए इस मामले में प्रतिलिपि की अपेक्षा की जानी चाहिए क्योंकि यह डिफ़ॉल्ट रूप से सी और सी ++ में स्ट्रक्चर असाइनमेंट कैसे काम करता है। जब तक 'struct1' 'readonly' फ़ील्ड न हो, तब तक यह संकलन समय पर असफल हो जाएगा। – Groo

+0

वैसे भी, उपयोगी जानकारी के लिए +1, लेकिन मैं उस हिस्से को थोड़ा सा फिर से बदल दूंगा जहां आप कहते हैं कि * केवल पढ़ने की संरचना पर एक इंस्टेंस विधि को कॉल करने से संरचना की प्रतिलिपि बन जाएगी *, क्योंकि यह * * केवल पढ़ने के लिए * 'संरचना क्षेत्र उस संरचना की एक प्रति बनाता है। यदि आप पहले स्थानीय चर में मान पढ़ते हैं, तो आप इसे म्यूट कर सकते हैं (जब तक इसके आंतरिक फ़ील्ड म्यूटेबल होते हैं) और .NET सीधे स्टैक आधारित संरचना पर काम करेगा ("कार्यान्वयन विवरण", मुझे पता है)। जब कोई फ़ील्ड 'रीडोनली' नहीं होता है, तो विधियों को सीधे प्रतिलिपि बनाने की आवश्यकता के बिना मैदान पर काम कर सकते हैं। – Groo

+0

@ ग्रूओ: वहां बहुत सारे कोने के मामले नहीं हैं, जहां यह महत्वपूर्ण है कि 'struct1 = struct2' अपने मौजूदा क्षेत्रों को ओवरराइट करके * मौजूदा * स्ट्रक्चर 1 को बदलता है, लेकिन एकमात्र तरीका यह है कि कोई भी उन कोने मामलों को सही ढंग से समझ सकता है क्या समझ रहा है वास्तव में हो रहा है। आपके दूसरे बिंदु के संबंध में, मैं बिल्कुल स्पष्ट नहीं हूं कि आप क्या सुझाव दे रहे हैं; वाक्यांश "रीड-ओनली स्ट्रक्चर" दोनों चरों को संदर्भित करता है जिनमें 'रीडोनली' क्वालीफायर होता है, और अस्थायी संग्रहण स्थान जैसे फ़ंक्शन या प्रॉपर्टी रिटर्न वैल्यू; क्या मुझे यह ध्यान रखना होगा कि पढ़ने/लिखने वाले गुणों के वापसी मूल्य केवल पढ़ने के लिए हैं? – supercat

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