2008-11-04 13 views
44

मैंने कुछ परियोजनाओं के दौरान अपरिवर्तनीय (रीडोनली) ऑब्जेक्ट्स और अपरिवर्तनीय ऑब्जेक्ट ग्राफ़ बनाने के लिए एक पैटर्न विकसित किया है। अपरिवर्तनीय वस्तुओं को 100% धागा सुरक्षित होने का लाभ होता है और इसलिए धागे में पुन: उपयोग किया जा सकता है। मेरे काम में मैं अक्सर कॉन्फ़िगरेशन सेटिंग्स और अन्य ऑब्जेक्ट्स के लिए वेब अनुप्रयोगों में इस पैटर्न का उपयोग करता हूं जो मैं लोड करता हूं और स्मृति में कैश करता हूं। कैश किए गए ऑब्जेक्ट्स हमेशा अपरिवर्तनीय होना चाहिए क्योंकि आप गारंटी देना चाहते हैं कि वे अप्रत्याशित रूप से परिवर्तित नहीं हुए हैं।सी # में अपरिवर्तनीय वस्तु पैटर्न - आपको क्या लगता है?

अब, आप निश्चित रूप से आसानी से निम्न उदाहरण में अपरिवर्तनीय वस्तुओं डिजाइन कर सकते हैं:

public class SampleElement 
{ 
    private Guid id; 
    private string name; 

    public SampleElement(Guid id, string name) 
    { 
    this.id = id; 
    this.name = name; 
    } 

    public Guid Id 
    { 
    get { return id; } 
    } 

    public string Name 
    { 
    get { return name; } 
    } 
} 

यह सरल वर्गों के लिए ठीक है - लेकिन और अधिक जटिल वर्गों के लिए मैं के माध्यम से सभी मान पास करने की अवधारणा पसंद नहीं करते एक निर्माता गुणों पर सेटर्स रखना अधिक वांछनीय है और एक नया ऑब्जेक्ट बनाने वाला आपका कोड पढ़ने में आसान हो जाता है।

तो आप सेटर्स के साथ अपरिवर्तनीय वस्तुएं कैसे बनाते हैं?

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

ठीक है, अब कुछ कोड पर। मेरे पास निम्न कोड स्निपेट्स में पैटर्न को अपने सबसे सरल रूप में उबालने की कोशिश की गई है। आईलेमेंट बेस इंटरफेस है कि सभी अपरिवर्तनीय वस्तुओं को अंततः लागू करना चाहिए।

public class SampleElement : Element 
{ 
    private Guid id; 
    private string name; 

    public SampleElement() {} 

    public Guid Id 
    { 
    get 
    { 
     return id; 
    } 
    set 
    { 
     FailIfImmutable(); 
     id = value; 
    } 
    } 

    public string Name 
    { 
    get 
    { 
     return name; 
    } 
    set 
    { 
     FailIfImmutable(); 
     name = value; 
    } 
    } 
} 

अब आप कर सकते हैं परिवर्तन:

public abstract class Element : IElement 
{ 
    private bool immutable; 

    public bool IsReadOnly 
    { 
    get { return immutable; } 
    } 

    public virtual void MakeReadOnly() 
    { 
    immutable = true; 
    } 

    protected virtual void FailIfImmutable() 
    { 
    if (immutable) throw new ImmutableElementException(this); 
    } 

    ... 
} 

के अपरिवर्तनीय वस्तु पैटर्न लागू करने के लिए ऊपर SampleElement वर्ग refactor करते हैं:

public interface IElement : ICloneable 
{ 
    bool IsReadOnly { get; } 
    void MakeReadOnly(); 
} 

तत्व वर्ग IElement इंटरफेस के डिफ़ॉल्ट कार्यान्वयन है आईडी संपत्ति और नाम संपत्ति जब तक ऑब्जेक्ट को MakeReadOnly() विधि को कॉल करके अपरिवर्तनीय के रूप में चिह्नित नहीं किया गया है। एक बार यह अपरिवर्तनीय हो जाने पर, एक सेटर को कॉल करने से एक अपरिवर्तनीय एलिमेंट अपवाद उत्पन्न होगा।

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

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

+8

.NET Framework दिशानिर्देश ILloneable को लागू करने या सार्वजनिक API में इसका उपयोग करने की अनुशंसा नहीं करते हैं, क्योंकि आईसीएलनेबल का अनुबंध अनुबंध को संतुष्ट करने के लिए आवश्यक क्लोन कार्यान्वयन के प्रकार को निर्दिष्ट नहीं करता है। –

+2

यदि आप WPF का उपयोग कर रहे हैं, तो इनमें से अधिकतर फ्रीजबल क्लास से उतरकर पहले ही उपलब्ध है। –

+0

Thx जो - इस कक्षा के बारे में नहीं पता था। बहुत अच्छा लग रहा है। –

उत्तर

30

जानकारी के लिए, दूसरे दृष्टिकोण को "पॉपसिकल अपरिवर्तनीयता" कहा जाता है।

एरिक लिपर्ट में here से अपरिवर्तनीयता पर ब्लॉग प्रविष्टियों की एक श्रृंखला है। मैं अभी भी सीटीपी (सी # 4.0) के साथ पकड़ रहा हूं, लेकिन यह दिलचस्प लगता है कि वैकल्पिक/नामित पैरामीटर (.ctor में) यहां क्या कर सकता है (जब रीडोनली फ़ील्ड में मैप किया जाता है) ... [अपडेट: मेरे पास है इस here पर ब्लॉग किया गया]

जानकारी के लिए, शायद मैं उन विधियों को नहीं बनाऊंगा virtual - हम शायद नहीं चाहते कि सबक्लास इसे गैर-मुक्त करने में सक्षम हों। AOP (जैसे PostSharp के रूप में) उन सभी ThrowIfFrozen() चेकों को जोड़ने के लिए एक व्यवहार्य विकल्प हो सकता है -

[public|protected] void Freeze() 
{ 
    if(!frozen) 
    { 
     frozen = true; 
     OnFrozen(); 
    } 
} 
protected virtual void OnFrozen() {} // subclass can add code here. 

इसके अलावा: आप उन्हें अतिरिक्त कोड जोड़ने में सक्षम होना चाहते हैं, मैं की तरह कुछ सुझाव देना चाहेंगे।

(क्षमा याचना अगर मैं शब्दावली/विधि नाम बदल गए हैं - तो मूल पोस्ट दिखाई जब उत्तर रचना नहीं रखता)

+0

धन्यवाद - बस वह लिंक जो मैं ढूंढ रहा था। एरिक बहुत सटीक वर्णन करता है कि जब आप सर्कुलर संदर्भ और क्रमबद्धता का उपयोग कर रहे हैं तो यह उपयोगी क्यों होता है। –

+0

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

+0

मुझे लगता है कि कुछ शब्द देने के लिए यह एक अच्छा विचार होगा कि "popsicle immutability" क्या है, इसके संदर्भ में इसके अलावा। बस इसलिए किसी को यह समझ हो सकता है कि यह केवल उत्तर के पाठ से सही उत्तर क्यों है। अगर संभव हो तो। धन्यवाद। –

8

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

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

4

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

+0

मुझे नहीं पता कि यह इस तरह व्यवहार किया गया है, एक दिलचस्प पैटर्न! –

+0

बस डबल धार वाली तलवार से सावधान रहें; जीसी के बारे में चिंता करने के लिए आप बहुत सारी gen0 ऑब्जेक्ट्स प्राप्त कर सकते हैं। जाहिर है यह प्रत्येक के आकार (इसलिए स्ट्रिंगबिल्डर) के कारण स्ट्रिंग के लिए विशेष रूप से खराब है, लेकिन यह अभी भी एक धाराप्रवाह उत्परिवर्ती API के लिए एक विचार है। –

2

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

+0

जब भी मेरे पास कॉन्फ़िगरेशन सेटिंग्स और इसी तरह के लिए एक बड़ा ऑब्जेक्ट मॉडल होता है तो मैं मूल रूप से इस पैटर्न का उपयोग करता हूं। ऑब्जेक्ट मॉडल को XML पर क्रमबद्ध किया जा सकता है और ऑब्जेक्ट पर फिर से एक्सएमएलसेरियलाइज़र क्लास का उपयोग करके ऑब्जेक्ट्स पर सेटर्स की आवश्यकता होती है। –

+0

क्या आप स्पष्टीकरण दे सकते हैं? VALUES का प्रतिनिधित्व करने वाली वस्तुओं को पूरी तरह से * im * mutable होना चाहिए। ऑब्जेक्ट को अपरिवर्तनीय बनाते हुए, यह केवल साधन-टू-एंड है। अंत एक अपरिवर्तनीय वर्ग है, जो बहु-थ्रेडेड/कार्यात्मक कोड के लिए अत्यधिक वांछनीय है। –

+0

सर्कुलर संदर्भों और क्रमबद्धता की मेरी आवश्यकता मूल रूप से दो उत्तरों के बारे में चर्चा के रूप में popsicle अपरिवर्तनीयता के लिए कहते हैं। –

9

इस संशोधन के बारे में मेरी प्रारंभिक असुविधा के बाद मुझे प्रत्येक संशोधन पर नया System.Drawing.Point बनाना पड़ा, मैंने कुछ साल पहले पूरी तरह से अवधारणा को गले लगा लिया था। असल में, अब मैं डिफ़ॉल्ट रूप से readonly के रूप में प्रत्येक फ़ील्ड बना देता हूं और केवल एक परिवर्तनीय कारण होने पर इसे परिवर्तित करने के लिए बदलता है - जो आश्चर्यजनक रूप से शायद ही कभी है।

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

17

एक और विकल्प किसी प्रकार का बिल्डर क्लास बनाना होगा।

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

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

यह थोड़ा और कोड है, लेकिन मुझे लगता है कि यह एक वर्ग पर सेटर्स होने की तुलना में क्लीनर है जो अपरिवर्तनीय माना जाता है।

+0

सही किया गया, हां, एक तत्व निर्माता एक अच्छा विचार है। मैं इस समाधान के लिए नहीं गया क्योंकि मेरे ऑब्जेक्ट मॉडल को एक्सएमएल में परिवर्तित किया जा सकता है और फिर एक्सएमएलसेरियलाइज़र क्लास का उपयोग करके ऑब्जेक्ट्स पर फिर से ऑब्जेक्ट्स पर सेटर्स की आवश्यकता होती है। –

+1

यह दृष्टिकोण, वैध होने पर, बनाए रखने के लिए कोड की मात्रा पर महत्वपूर्ण प्रभाव डालता है। कोड जनरेटर उपकरण के लिए बढ़िया; मनुष्यों के लिए इतना अच्छा नहीं है। –

6

(अपेक्षाकृत) नया सॉफ्टवेयर डिज़ाइन प्रतिमान जिसे डोमेन संचालित डिज़ाइन कहा जाता है, इकाई वस्तुओं और मूल्य वस्तुओं के बीच अंतर बनाता है।

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

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

+0

मुझे एंटिटी/वैल्यू डिस्टिनेशन पसंद है, और यह सकारात्मक होगा कि सभी ऑब्जेक्ट्स के लिए समानता के अर्थ के लिए समझदार रूप से 'बराबर' परिभाषित कर सकता है। दो इकाई संदर्भ एक दूसरे के बराबर होंगे यदि वे एक ही इकाई को लक्षित करते हैं, जबकि मूल्य संदर्भ समकक्ष मूल्यों को लक्षित करते हैं तो मूल्य समकक्ष समकक्ष होंगे। कोई गैर-शून्य इकाई संदर्भ किसी भी गैर-शून्य मान संदर्भ के बराबर है। .NET Framework में कुछ प्रकार के खराब समानता के अलावा कुछ और मतलब के बराबर परिभाषित करते हैं। – supercat

4

@Cory Foy और @Charles Bretana द्वारा बिंदु पर विस्तार जहां के बीच एक अंतर है संस्थाओं और मूल्यों। जबकि मूल्य-वस्तुओं को हमेशा अपरिवर्तनीय होना चाहिए, मैं वास्तव में नहीं सोचता कि एक वस्तु स्वयं को स्थिर करने में सक्षम होनी चाहिए, या कोडेबेस में मनमाने ढंग से जमे हुए होने की अनुमति देनी चाहिए। इसमें बहुत बुरा गंध है, और मुझे चिंता है कि यह पता लगाने में मुश्किल हो सकती है कि वास्तव में एक वस्तु जमे हुए थी, और यह क्यों जमे हुए थे, और तथ्य यह है कि किसी ऑब्जेक्ट को कॉल करने के बीच यह राज्य को ठंडा से जमे हुए ।

यह कहना नहीं है कि कभी-कभी आप किसी (म्यूटेबल) इकाई को कुछ देना चाहते हैं और यह सुनिश्चित करना है कि यह बदला नहीं जा रहा है।

तो, बजाय वस्तु ही ठंड की, एक और संभावना ReadOnlyCollection के शब्दों को कॉपी करने के < टी है>

List<int> list = new List<int> { 1, 2, 3}; 
ReadOnlyCollection<int> readOnlyList = list.AsReadOnly(); 

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

ध्यान दें कि ReadOnlyCollection < टी> आईसीओलेक्शन < टी> लागू करता है जिसमें इंटरफ़ेस में Add(T item) विधि है। हालांकि इंटरफ़ेस में परिभाषित bool IsReadOnly { get; } भी है ताकि उपभोक्ता एक विधि को कॉल करने से पहले जांच सकें जो अपवाद फेंक देगा।

अंतर यह है कि आप केवल ISRead को झूठी नहीं सेट कर सकते हैं। एक संग्रह या तो पढ़ा जाता है या केवल पढ़ा नहीं जाता है, और जो संग्रह के जीवनकाल में कभी नहीं बदलता है।

यह समय अच्छा होगा कि सी ++ आपको संकलन समय पर देता है, लेकिन यह समस्या का अपना सेट होने लगता है और मुझे खुशी है कि सी # वहां नहीं जाता है।


ICloneable - मैंने सोचा कि मैं सिर्फ निम्नलिखित से देख चाहते हैं:

ICloneable को लागू न करें

सार्वजनिक APIs में ICloneable का प्रयोग न करें

Brad Abrams - Design Guidelines, Managed code and the .NET Framework

+3

मुझे इस बात से सहमत नहीं है कि इसमें खराब गंध है। अपरिवर्तनीयता के इस रूप को मार्क द्वारा इंगित किए गए "पॉपसिकल अपरिवर्तनीयता" के रूप में जाना जाता है और कुछ परिदृश्यों में बहुत उपयोगी है। मैं बस इस प्रकार की अपरिवर्तनीय वस्तुओं का समर्थन करने वाले कंपाइलर की कामना करता हूं। –

+0

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

+0

ReadOnlyCollection को देखने के लिए मेरा सुझाव है क्योंकि मैं एक विधि कॉल पर स्पष्टीकरण पसंद करता हूं जो किसी ऑब्जेक्ट को बता सकता है कि यह अब परिवर्तनीय नहीं है। हालांकि मैं अपरिवर्तनीयता के अधिक सामान्य मामले में भी बात कर रहा हूं। –

2

तत्व गुणों को सरल बनाने के लिए बस एक युक्ति: automatic propertiesprivate set के साथ उपयोग करें और स्पष्ट रूप से डेटा फ़ील्ड घोषित करने से बचें। जैसे

public class SampleElement { 
    public SampleElement(Guid id, string name) { 
    Id = id; 
    Name = name; 
    } 

    public Guid Id { 
    get; private set; 
    } 

    public string Name { 
    get; private set; 
    } 
} 
+0

टिप के लिए धन्यवाद - सी # 3.0 में एक और निफ्टी छोटी सुविधा :-) मैं निश्चित रूप से निजी सेटर्स के साथ जाऊंगा यदि ऐसा नहीं था क्योंकि मुझे XmlSerializer का उपयोग करके मेरे ऑब्जेक्ट ग्राफ़ को क्रमबद्ध/deserialize करने में सक्षम होना चाहिए। –

+0

अच्छा होगा अगर XmlSerializer गुणों के साथ कन्स्ट्रक्टर paramaters को सहसंबंध करने के लिए पर्याप्त चालाक थे और डिफ़ॉल्ट कन्स्ट्रक्टर के बजाय उस कन्स्ट्रक्टर का उपयोग कर ऑब्जेक्ट का निर्माण –

2

चैनल 9 पर एक नया वीडियो है जहां साक्षात्कार में 36:30 से एंडर्स हेजल्सबर्ग सी # में अपरिवर्तनीयता के बारे में बात करना शुरू कर देता है। वह popsicle अपरिवर्तनीयता के लिए एक बहुत अच्छा उपयोग मामला देता है और बताता है कि यह कुछ ऐसा है जो आपको वर्तमान में लागू करने के लिए आवश्यक है। यह सुनवाई उसे कहते हैं कि यह के भविष्य के संस्करणों में अपरिवर्तनीय वस्तु रेखांकन बनाने के लिए बेहतर समर्थन के बारे में सोच के लायक है मेरी कानों में संगीत था सी #

Expert to Expert: Anders Hejlsberg - The Future of C#

2

अपने विशेष समस्या के लिए दो अन्य विकल्पों पर विचार-विमर्श नहीं किया गया है:

  1. अपना खुद का deserializer बनाएं, जो एक निजी संपत्ति सेटर को कॉल कर सकता है। हालांकि शुरुआत में deserializer बनाने में प्रयास बहुत अधिक होगा, यह चीजों को क्लीनर बनाता है। कंपाइलर आपको सेटर्स को कॉल करने का प्रयास करने से भी बचाएगा और आपके कक्षाओं में कोड पढ़ने में आसान होगा।

  2. प्रत्येक कक्षा में एक कंस्ट्रक्टर रखें जो XElement (या XML ऑब्जेक्ट मॉडल का कुछ अन्य स्वाद) लेता है और इसे स्वयं से पॉप्युलेट करता है। जाहिर है जैसे वर्गों की संख्या बढ़ जाती है, यह समाधान के रूप में जल्दी से कम वांछनीय हो जाता है।

4

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

आप एक आंशिक वर्ग उत्पन्न करेंगे जिसमें सभी फ्रीजेबल गुण शामिल हैं। इसके लिए एक पुन: प्रयोज्य टी 4 टेम्पलेट बनाने के लिए यह काफी आसान होगा।

टेम्पलेट इनपुट के लिए इस ले जाएगा:

  • नाम स्थान
  • वर्ग के नाम
  • संपत्ति नाम/प्रकार tuples की सूची

और होगा उत्पादन एक सी # फ़ाइल, युक्त

  • नेमस्पेस घोषणा
  • आंशिक वर्ग
  • इसी प्रकार, एक समर्थन क्षेत्र, एक गेटर, और एक सेटर जो FailIfFrozen विधि freezable गुण भी काम कर सकता था पर

AOP टैग आह्वान के साथ

  • संपत्तियों में से प्रत्येक,, लेकिन यह अधिक निर्भरताओं की आवश्यकता होगी, जबकि टी 4 विजुअल स्टूडियो के नए संस्करणों में बनाया गया है।

    एक और परिदृश्य जो इस तरह है INotifyPropertyChanged इंटरफ़ेस है। उस समस्या के समाधान इस समस्या पर लागू होने की संभावना है।

  • 2

    उप-वर्गीकरण MutableThing और ImmutableThing के साथ एक अमूर्त वर्ग ThingBase होने के बारे में कैसे? थिंगबेस में संरक्षित संरचना में सभी डेटा शामिल होंगे, जो फ़ील्ड के लिए सार्वजनिक पढ़ने-योग्य गुण प्रदान करते हैं और इसकी संरचना के लिए केवल पढ़ने योग्य संपत्ति ही प्रदान करते हैं। यह एक अतिसंवेदनशील AsImutable विधि भी प्रदान करेगा जो एक अपरिवर्तनीय टिंग लौटाएगा।

    MutableThing गुणों को पढ़ने/लिखने वाले गुणों के साथ छाया देगा, और एक डिफ़ॉल्ट कन्स्ट्रक्टर और एक कन्स्ट्रक्टर प्रदान करेगा जो थिंगबेस स्वीकार करता है।

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

    2

    आप वैकल्पिक नामित तर्कों का उपयोग नलिकाओं के साथ एक छोटे से बॉयलरप्लेट के साथ एक अपरिवर्तनीय सेटटर बनाने के लिए कर सकते हैं। यदि आप वास्तव में संपत्ति को शून्य पर सेट करना चाहते हैं तो आपको कुछ और परेशानी हो सकती हैं।

    class Foo{ 
        ... 
        public Foo 
         Set 
         (double? majorBar=null 
         , double? minorBar=null 
         , int?  cats=null 
         , double?  dogs=null) 
        { 
         return new Foo 
          (majorBar ?? MajorBar 
          , minorBar ?? MinorBar 
          , cats  ?? Cats 
          , dogs  ?? Dogs); 
        } 
    
        public Foo 
         (double R 
         , double r 
         , int l 
         , double e 
         ) 
        { 
         .... 
        } 
    } 
    

    तुम इतनी

    var f = new Foo(10,20,30,40); 
    var g = f.Set(cat:99); 
    
    3

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

    कारण है कि मैं इस तरह एक सामान्य वर्ग के रूप में एक संकलन समय संयम के साथ इस कोडिंग प्रतिमान विस्तृत होता है, है यही कारण है कि:

    // All elements of this list are guaranteed to be immutable 
    List<Immutable<SampleElement>> elements = 
        new List<Immutable<SampleElement>>(); 
    
    for (int i = 1; i < 10; i++) 
    { 
        SampleElement newElement = new SampleElement(); 
        newElement.Id = Guid.NewGuid(); 
        newElement.Name = "Sample" + i.ToString(); 
    
        // The compiler will automatically convert to Immutable<SampleElement> for you 
        // because of the implicit conversion operator 
        elements.Add(newElement); 
    } 
    
    foreach (SampleElement element in elements) 
        Console.Out.WriteLine(element.Name); 
    
    elements[3].Value.Id = Guid.NewGuid();  // This will throw an ImmutableElementException 
    
    :

    public class Immutable<T> where T : IElement 
    { 
        private T value; 
    
        public Immutable(T mutable) 
        { 
         this.value = (T) mutable.Clone(); 
         this.value.MakeReadOnly(); 
        } 
    
        public T Value 
        { 
         get 
         { 
          return this.value; 
         } 
        } 
    
        public static implicit operator Immutable<T>(T mutable) 
        { 
         return new Immutable<T>(mutable); 
        } 
    
        public static implicit operator T(Immutable<T> immutable) 
        { 
         return immutable.value; 
        } 
    } 
    

    यहां एक नमूना है कि कैसे आप इस का प्रयोग करेंगे है

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