बेस्ट प्रैक्टिस रिटर्निंग रीड-ओनली ऑब्जेक्ट


11

मेरे पास C # में OOP के बारे में "सर्वोत्तम अभ्यास" सवाल है (लेकिन यह सभी भाषाओं पर लागू होता है)।

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

class A
{
    // Note: List is just example, I am interested in objects in general.
    private List<string> _items = new List<string>() { "hello" }

    public List<string> Items
    {
        get
        {
            // Option A (not read-only), can be modified from outside:
            return _items;

            // Option B (sort-of read-only):
            return new List<string>( _items );

            // Option C, must change return type to ReadOnlyCollection<string>
            return new ReadOnlyCollection<string>( _items );
        }
    }
}

स्पष्ट रूप से सबसे अच्छा दृष्टिकोण "विकल्प सी" है, लेकिन बहुत कम वस्तुओं में ReadOnly संस्करण है (और निश्चित रूप से कोई उपयोगकर्ता-परिभाषित कक्षाएं नहीं हैं)।

यदि आप वर्ग A के उपयोगकर्ता थे, तो क्या आप परिवर्तनों की अपेक्षा करेंगे

List<string> someList = ( new A() ).Items;

मूल (ए) वस्तु के लिए प्रचार? या क्या किसी क्लोन को वापस करना ठीक है बशर्ते कि वह टिप्पणियों / प्रलेखन में ऐसा लिखा गया हो? मुझे लगता है कि इस क्लोन दृष्टिकोण से ट्रैक करने में काफी मुश्किल हो सकती है।

मुझे याद है कि C ++ में हम कास्ट ऑब्जेक्ट्स को वापस कर सकते हैं और आप केवल उस पर कॉन्स्ट के रूप में चिह्नित विधियों को कॉल कर सकते हैं। मुझे लगता है कि C # में ऐसा कोई फीचर / पैटर्न नहीं है? यदि नहीं, तो वे इसे शामिल क्यों नहीं करेंगे? मेरा मानना ​​है कि इसे कांस्ट शुद्धता कहा जाता है।

लेकिन फिर मेरा मुख्य प्रश्न "आप क्या उम्मीद करेंगे" के बारे में है या विकल्प ए बनाम बी को कैसे संभालना है।


2
.NET 4.5 के लिए नए अपरिवर्तनीय संग्रह के इस पूर्वावलोकन के बारे में इस लेख पर एक नज़र डालें: blogs.msdn.com/b/bclteam/archive/2012/12/18/… आप उन्हें पहले से ही NuGet के माध्यम से प्राप्त कर सकते हैं।
क्रिस्टोफ क्लेज

जवाबों:


10

@ जेसी के जवाब के अलावा, जो संग्रह के लिए सबसे अच्छा समाधान है: सामान्य विचार ए के अपने सार्वजनिक इंटरफ़ेस को इस तरह से डिजाइन करना है ताकि यह केवल वापस आए

  • मूल्य प्रकार (जैसे इंट, डबल, स्ट्रक्चर)

  • अपरिवर्तनीय वस्तुएं (जैसे "स्ट्रिंग", या उपयोगकर्ता परिभाषित कक्षाएं जो केवल आपकी कक्षा ए की तरह केवल पढ़ने के लिए तरीके प्रदान करती हैं)

  • (केवल अगर आपको चाहिए): ऑब्जेक्ट प्रतियां, जैसे आपका "विकल्प बी" प्रदर्शित करता है

IEnumerable<immutable_type_here> अपने आप से अपरिवर्तनीय है, इसलिए यह उपरोक्त का एक विशेष मामला है।


19

यह भी ध्यान दें कि आपको इंटरफेस से प्रोग्रामिंग करना चाहिए, और बड़े और उस संपत्ति से जो आप वापस करना चाहते हैं, वह ए है IEnumerable<string>। A List<string>थोड़ी संख्या में नहीं है क्योंकि यह आम तौर पर परिवर्तनशील है और मैं यह कहने के लिए इतना आगे जाऊंगा कि यह महसूस ReadOnlyCollection<string>करने वाले के कार्यान्वयन के रूप में ICollection<string>यह Liskov प्रतिस्थापन सिद्धांत का उल्लंघन करता है।


6
+1, a का उपयोग IEnumerable<string>करना ठीक वैसा ही है जैसा कोई चाहता है।
डॉक्टर ब्राउन

2
.Net 4.5 में, आप भी उपयोग कर सकते हैं IReadOnlyList<T>
svick

3
अन्य इच्छुक पार्टियों के लिए ध्यान दें। प्रॉपर्टी ऐक्सेसर पर विचार करते समय: यदि आप लिस्ट के बजाय IEnumerable <string> को लिस्ट <string> पर लौटाते हैं तो _list ((IEnumerable को निहित कास्ट) कर सकते हैं, तो इस लिस्ट को वापस लिस्ट में डाला जा सकता है <string> और बदला और बदल जाएगा मुख्य वस्तु में प्रचार करना। आप नई सूची वापस कर सकते हैं <string> (_list) (IEnumerable करने के लिए + बाद में निहित कलाकारों) जो आंतरिक सूची की रक्षा करेगा। इस पर अधिक यहां पाया जा सकता है: stackoverflow.com/questions/4056013/…
NeverStopLearning

1
क्या यह आवश्यक है कि किसी संग्रह के किसी भी प्राप्तकर्ता को, जिसे इंडेक्स द्वारा एक्सेस करने की आवश्यकता हो, उसे डेटा को एक प्रकार से कॉपी करने के लिए तैयार होना चाहिए जो उसे सुविधा प्रदान करता है, या क्या यह बेहतर है कि कोड को वापस करने वाला कोड उसे इस रूप में प्रदान करे। सूचकांक द्वारा सुलभ है? जब तक आपूर्तिकर्ता के पास एक ऐसे रूप में होने की संभावना होती है जो सूचकांक द्वारा पहुंच की सुविधा नहीं देता है, मैं यह विचार करूंगा कि प्राप्तकर्ताओं को अपनी अनावश्यक प्रति बनाने की आवश्यकता से मुक्त करने का मूल्य आपूर्तिकर्ता को आपूर्ति करने से मुक्त करने के मूल्य से अधिक होगा। रैंडम-एक्सेस फॉर्म (जैसे एक केवल पढ़ने के लिए IList<T>)
सुपरकैट

2
एक बात जो मुझे IEnumerable के बारे में पसंद नहीं है, वह यह है कि आप कभी नहीं जानते कि यह कोरटाइन के रूप में चलती है या क्या यह सिर्फ एक डेटा ऑब्जेक्ट है। यदि यह एक coroutine के रूप में चलता है तो इसके परिणामस्वरूप सूक्ष्म कीड़े हो सकते हैं इसलिए कभी-कभी यह जानना अच्छा होता है। यही कारण है कि मैं ReadOnlyCollection या IReadOnlyList इत्यादि को पसंद करता हूं
स्टीव वर्म्यूलेन

3

मुझे थोड़ी देर पहले भी इसी तरह की समस्या का सामना करना पड़ा था और नीचे मेरा समाधान था, लेकिन यह वास्तव में केवल तभी काम करता है जब आप लाइब्रेरी को नियंत्रित करते हैं:


अपनी कक्षा से शुरुआत करें A

public class A
{
    private int _n;
    public int N
    {
        get { return _n; }
        set { _n = value; }
    }
}

इसके बाद इसमें से एक इंटरफेस प्राप्त करें जिसमें केवल संपत्तियों का हिस्सा मिलता है (और कोई पढ़ने के तरीके) और ए को लागू करें

public interface IReadOnlyA
{
    public int N { get; }
}
public class A : IReadOnlyA
{
    private int _n;
    public int N
    {
        get { return _n; }
        set { _n = value; }
    }
}

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


2
इसके साथ समस्या यह है, कि इसे वापस उत्परिवर्तित संस्करण में डालना संभव है। और एलएसपी का इसका उल्लंघन, बेस क्लास (इस मामले में इंटरफ़ेस के) तरीकों के माध्यम से देखने योग्य राज्य को उपवर्ग से नए तरीकों द्वारा बदला जा सकता है। लेकिन यह कम से कम इरादे को व्यक्त करता है, भले ही वह इसे लागू न करे।
user470365

1

किसी को भी, जो इस सवाल का पता लगाता है और फिर भी जवाब में दिलचस्पी रखता है - अब एक और विकल्प है जो उजागर हो रहा है ImmutableArray<T>। ये कुछ बिंदु हैं जो मुझे लगता है कि यह बेहतर है IEnumerable<T>या ReadOnlyCollection<T>:

  • यह स्पष्ट रूप से बताता है कि यह अपरिवर्तनीय है (अभिव्यंजक API)
  • यह स्पष्ट रूप से बताता है कि संग्रह पहले से ही बना हुआ है (दूसरे शब्दों में यह प्रारंभिक रूप से आलसी नहीं है और कई बार इसे पुनरावृत्त करना ठीक है)
  • यदि वस्तुओं की गिनती ज्ञात है, तो यह उस तरह की जानकारी नहीं छिपाती IEnumerable<T>है
  • चूँकि क्लाइंट को यह पता नहीं होता है कि उसका कार्यान्वयन क्या है IEnumerableया IReadOnlyCollectवह यह नहीं मान सकता है कि यह कभी नहीं बदलता (दूसरे शब्दों में इस बात की कोई गारंटी नहीं है कि संग्रह के आइटम को बदला नहीं गया है जबकि उस संग्रह का संदर्भ अपरिवर्तित रहता है)

इसके अलावा, अगर एपीआई को कलेक्शन में बदलाव के बारे में क्लाइंट को सूचित करना है, तो मैं एक अलग सदस्य के रूप में एक घटना का खुलासा करूंगा।

ध्यान दें कि मैं यह नहीं कह रहा हूं कि IEnumerable<T>सामान्य रूप से बुरा है। मैं अभी भी इसका उपयोग तब करूंगा जब संग्रह को एक तर्क के रूप में पारित कर दिया जाए या लाज़िली आरंभीकृत संग्रह लौटाया जाए (इस विशेष मामले में यह आइटम गणना को छिपाने के लिए समझ में आता है क्योंकि यह अभी तक ज्ञात नहीं है)।

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