2008-08-29 19 views
43

हर बार जब मैं एक ऑब्जेक्ट बनाता हूं जिसमें संग्रह संपत्ति है तो मैं इसे करने के सर्वोत्तम तरीके पर आगे और आगे जाता हूं?संग्रह संपत्ति का खुलासा कैसे करें?

    एक गेटर कि निजी चर के लिए एक संदर्भ रिटर्न
  1. स्पष्ट get_ObjList और set_ObjList तरीकों कि लौट सकते हैं और नए या क्लोन वस्तुओं हर बार बनाने के साथ
  2. सार्वजनिक संपत्ति
  3. स्पष्ट get_ObjList कि एक IEnumerator वापस आती है और एक set_ObjList कि IENumerator

संग्रह में कोई फर्क पड़ता है यदि संग्रह है एक सरणी (यानी, objList.Clone()) एक सूची बनाम?

यदि संदर्भ के रूप में वास्तविक संग्रह को वापस करना इतना बुरा है क्योंकि यह निर्भरता बनाता है, तो संदर्भ के रूप में किसी भी संपत्ति को वापस क्यों करें? जब भी आप किसी बच्चे के ऑब्जेक्ट को किसी संदर्भ के रूप में बेनकाब करते हैं तो उस बच्चे के आंतरिक भाग को माता-पिता के बिना "जानना" बदला जा सकता है जब तक कि बच्चे के पास संपत्ति बदल नहीं जाती है। क्या स्मृति रिसाव के लिए कोई खतरा है?

और, 2 और 3 ब्रेक धारावाहिक विकल्प नहीं हैं? क्या यह एक पकड़ 22 है या क्या आपको कस्टम सीरियलाइजेशन लागू करना होगा जब भी आपके पास संग्रह संपत्ति हो?

सामान्य रीडऑनली कोलेक्शन सामान्य उपयोग के लिए एक अच्छा समझौता जैसा प्रतीत होता है। यह एक IList लपेटता है और इसके उपयोग को प्रतिबंधित करता है। शायद यह स्मृति रिसाव और क्रमबद्धता में मदद करता है। हालांकि इसमें अभी भी enumeration concerns

शायद यह केवल निर्भर करता है। यदि आपको परवाह नहीं है कि संग्रह संशोधित है, तो बस इसे एक निजी चर के रूप में प्रति # 1 पर एक निजी चर के रूप में बेनकाब करें। यदि आप अन्य प्रोग्राम संग्रह को संशोधित नहीं करना चाहते हैं तो # 2 और/या # 3 बेहतर है।

प्रश्न में लागू यह है कि एक विधि को दूसरे तरीके से क्यों इस्तेमाल किया जाना चाहिए और सुरक्षा, स्मृति, क्रमबद्धता आदि पर क्या चलाना चाहिए?

उत्तर

52

आप संग्रह का पर्दाफाश कैसे करते हैं इस पर निर्भर करता है कि उपयोगकर्ताओं के साथ कैसे बातचीत करने का इरादा है।

private readonly Collection<T> myCollection_ = new ...; 
public Collection<T> MyCollection { 
    get { return this.myCollection_; } 
} 

इस रणनीति:

1) उपयोगकर्ताओं को जोड़ने हो जाएगा और एक वस्तु के संग्रह से आइटम निकालने के लिए, तो एक सरल केवल-get संग्रह संपत्ति मूल प्रश्न से # 1 सबसे अच्छा (विकल्प) है WindowsForms और WPF ItemsControl नियंत्रणों पर Items संग्रहों के लिए उपयोग किया जाता है, जहां उपयोगकर्ता उन आइटम्स को जोड़ते और निकालते हैं जिन्हें वे नियंत्रण प्रदर्शित करना चाहते हैं। ये नियंत्रण वास्तविक संग्रह प्रकाशित करते हैं और वस्तुओं का ट्रैक रखने के लिए कॉलबैक या ईवेंट श्रोताओं का उपयोग करते हैं।

WPF भी उपयोगकर्ताओं को ऐसी पर ItemsControlItemsSource संपत्ति (# 3 विकल्प मूल प्रश्न से) के रूप में आइटम वे नियंत्रित करते हैं, का एक संग्रह प्रदर्शित करने के लिए अनुमति देने के लिए कुछ settable संग्रह उजागर करता है। हालांकि, यह एक आम उपयोग मामला नहीं है।


2) उपयोगकर्ताओं को केवल जा वस्तु द्वारा बनाए रखा डाटा पढ़ने, तो आप एक केवल पढ़ने के लिए संग्रह का उपयोग कर सकते जाएगा, के रूप में Quibblesome सुझाव:

private readonly List<T> myPrivateCollection_ = new ...; 
private ReadOnlyCollection<T> myPrivateCollectionView_; 
public ReadOnlyCollection<T> MyCollection { 
    get { 
    if(this.myPrivateCollectionView_ == null) { /* lazily initialize view */ } 
    return this.myPrivateCollectionView_; 
    } 
} 

ध्यान दें कि ReadOnlyCollection<T> का लाइव दृश्य प्रदान करता है अंतर्निहित संग्रह, इसलिए आपको केवल एक बार दृश्य बनाना होगा।

आंतरिक संग्रह IList<T> को लागू नहीं करता है, या यदि आप और अधिक उन्नत उपयोगकर्ताओं के लिए उपयोग को सीमित करना चाहते हैं, आप के बजाय संग्रह के लिए उपयोग एक प्रगणक के माध्यम से लपेट कर सकते हैं:

public IEnumerable<T> MyCollection { 
    get { 
    foreach(T item in this.myPrivateCollection_) 
     yield return item; 
    } 
} 

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


3) अंत में, यदि आप उच्च स्तर के संग्रह के बजाय सरणियों को बेनकाब करने की जरूरत है, तो आप सरणी उन मूल प्रश्न से यह (विकल्प # 2 को संशोधित करने से रोकने के लिए की एक प्रति वापसी होना चाहिए):

private T[] myArray_; 
public T[] GetMyArray() { 
    T[] copy = new T[this.myArray_.Length]; 
    this.myArray_.CopyTo(copy, 0); 
    return copy; 
    // Note: if you are using LINQ, calling the 'ToArray()' 
    // extension method will create a copy for you. 
} 

आपको किसी संपत्ति के माध्यम से अंतर्निहित सरणी का खुलासा नहीं करना चाहिए, क्योंकि आप यह कहने में सक्षम नहीं होंगे कि उपयोगकर्ता इसे कब संशोधित करते हैं।सरणी को संशोधित करने की अनुमति देने के लिए, आप एक इसी SetMyArray(T[] array) विधि जोड़ सकते हैं या एक कस्टम इंडेक्सर का उपयोग करें:

public T this[int index] { 
    get { return this.myArray_[index]; } 
    set { 
    // TODO: validate new value; raise change event; etc. 
    this.myArray_[index] = value; 
    } 
} 

(जाहिर है, एक कस्टम इंडेक्सर को लागू करने से, आप बीसीएल वर्गों के काम को डुप्लिकेट हो जाएगा :)

+0

कृपया ध्यान दें कि सरणी ऑब्जेक्ट्स के मुक्केबाजी और अनबॉक्सिंग का कारण बनती हैं जो बहुत संसाधन गहन – Brettski

+8

है यदि सरणी दृढ़ता से टाइप की जाती है (यानी int [], long [], आदि), तत्वों तक पहुंच बॉक्सिंग का कारण नहीं बनती है। यह तभी होगा जब आप किसी ऑब्जेक्ट का उपयोग कर रहे थे [] मूल्य प्रकारों को स्टोर करने के लिए (यानी ऑब्जेक्ट [] a = new {1, 2, 3})। –

+0

आह, उचित बिंदु, आपने मेरे सुझाव में सुधार किया है। अच्छा है! :) – Quibblesome

0

यदि आप बस अपने उदाहरण पर एक संग्रह का पर्दाफाश करना चाहते हैं, तो एक निजी सदस्य चर के लिए गेटर/सेटर का उपयोग करना मेरे लिए सबसे समझदार समाधान (आपका पहला प्रस्तावित विकल्प) जैसा लगता है।

3

मैं आमतौर पर इस बात के लिए जाना, एक सार्वजनिक गेटर कि System.Collections.ObjectModel.ReadOnlyCollection रिटर्न:

public ReadOnlyCollection<SomeClass> Collection 
{ 
    get 
    { 
     return new ReadOnlyCollection<SomeClass>(myList); 
    } 
} 

और वस्तु पर सार्वजनिक तरीकों संग्रह संशोधित करने के लिए।

Clear(); 
Add(SomeClass class); 

वर्ग तो मैं बस के लिए # 1 के रूप में यह अपनी खुद की एपीआई लेखन की बचत होती है पद्धति के रूप में निजी चर का पर्दाफाश साथ गड़बड़ करने के लिए अन्य लोगों के लिए भंडार माना जाता है, लेकिन मुझे लगता है कि से संकोच करते हैं उत्पादन कोड में।

+1

यह संग्रह संपत्ति के प्रत्येक पढ़ने पर नया ReadOnlycollection बनाता है, जो बहुत संसाधन गहन – Ivan

+0

ऐई हो सकता है, सम्राट XLII पोस्ट किए गए उपर्युक्त उदाहरण में आधार को बेहतर बनाता है। – Quibblesome

+0

@ इवान वास्तव में नहीं, यह कन्स्ट्रक्टर को कॉल करने के बाद संसाधन गहन नहीं है क्योंकि यह कन्स्ट्रक्टर ओ (1) ऑपरेशन है, यह सिर्फ एक रैपर है। http://msdn.microsoft.com/en-us/library/ms132476(v=vs.110).aspx – ForceMagic

0

मैं जावा डेवलपर हूं लेकिन मुझे लगता है कि यह सी # के लिए समान है।

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

0

ReadOnlyCollection (T) का उपयोग करने का सुझाव आप एक समझौता क्यों करते हैं? यदि आपको अभी भी मूल लिपटे आईएलिस्ट पर परिवर्तन अधिसूचनाएं प्राप्त करने की आवश्यकता है तो आप अपने संग्रह को लपेटने के लिए ReadOnlyObservableCollection(T) का भी उपयोग कर सकते हैं। क्या यह आपके परिदृश्य में एक समझौता से कम होगा?

0

ReadOnlyCollection अभी भी नुकसान है कि उपभोक्ता यह सुनिश्चित नहीं कर सकता कि मूल संग्रह एक अयोग्य समय पर नहीं बदला जाएगा। इसके बजाय आप Immutable Collections का उपयोग कर सकते हैं। यदि आपको कोई परिवर्तन करने की आवश्यकता है तो मूल को बदलने के बजाय आपको एक संशोधित प्रति दी जा रही है। इसे लागू करने का तरीका यह परिवर्तनीय संग्रह के प्रदर्शन के साथ प्रतिस्पर्धी है। या इससे भी बेहतर यदि आपको प्रत्येक प्रतिलिपि के बाद कई अलग-अलग (असंगत) परिवर्तन करने के लिए कई बार मूल प्रतिलिपि बनाने की आवश्यकता नहीं है।

0

मैं संग्रह को बेनकाब करने के लिए नए IReadOnlyList<T> और IReadOnlyCollection<T> इंटरफेस का उपयोग करने की सलाह देता हूं (.NET 4.5 की आवश्यकता है)।

उदाहरण:

public class AddressBook 
{ 
    private readonly List<Contact> contacts; 

    public AddressBook() 
    { 
     this.contacts = new List<Contact>(); 
    } 

    public IReadOnlyList<Contact> Contacts { get { return contacts; } } 

    public void AddContact(Contact contact) 
    { 
     contacts.Add(contact); 
    } 

    public void RemoveContact(Contact contact) 
    { 
     contacts.Remove(contact); 
    } 
} 

आप गारंटी नहीं है कि संग्रह से नहीं हेरफेर किया जा सकता की जरूरत है बाहर तो ReadOnlyCollection<T> या नए अपरिवर्तनीय संग्रह पर विचार करें।

संग्रह को बेनकाब करने के लिए IEnumerable<T> इंटरफ़ेस का उपयोग करके से बचें। यह इंटरफ़ेस किसी भी गारंटी को परिभाषित नहीं करता है कि एकाधिक गणनाएं अच्छी तरह से प्रदर्शन करती हैं। यदि IENumerable एक क्वेरी का प्रतिनिधित्व करता है तो प्रत्येक गणना क्वेरी को फिर से निष्पादित करती है। डेवलपर जो IENumerable का उदाहरण प्राप्त करते हैं, यह नहीं जानते कि यह संग्रह या क्वेरी का प्रतिनिधित्व करता है या नहीं।

इस विषय के बारे में अधिक जानकारी इस Wiki page पर पढ़ी जा सकती है।

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