सदस्य कलेक्शन के खुलासे के लिए केवलपढ़नेयोग्यकलेक्शन या पहला असंख्य?


124

क्या किसी IEnumerable के बजाय ReadOnlyCollection के रूप में एक आंतरिक संग्रह को उजागर करने का कोई कारण है यदि कॉलिंग कोड केवल संग्रह पर पुनरावृत्त करता है?

class Bar
{
    private ICollection<Foo> foos;

    // Which one is to be preferred?
    public IEnumerable<Foo> Foos { ... }
    public ReadOnlyCollection<Foo> Foos { ... }
}


// Calling code:

foreach (var f in bar.Foos)
    DoSomething(f);

जैसा कि मैंने देखा कि IEnumerable ReadOnlyCollection के इंटरफ़ेस का एक सबसेट है और यह उपयोगकर्ता को संग्रह को संशोधित करने की अनुमति नहीं देता है। इसलिए यदि IEnumberable इंटरफ़ेस पर्याप्त है तो इसका उपयोग करने के लिए एक है। क्या इसके बारे में तर्क करने का एक उचित तरीका है या मैं कुछ याद कर रहा हूं?

धन्यवाद / एरिक


6
यदि आप .NET 4.5 का उपयोग कर रहे हैं, तो आप नए रीड-ओनली कलेक्शन इंटरफेस की कोशिश कर सकते हैं । आप अभी भी लौटे हुए संग्रह को लपेटना चाहते हैं ReadOnlyCollectionयदि आप पागल हैं, लेकिन अब आप एक विशिष्ट कार्यान्वयन से बंधे नहीं हैं।
स्ट्रिपिंगवर्यर

जवाबों:


96

अधिक आधुनिक समाधान

जब तक आपको आंतरिक संग्रह को म्यूट करने की आवश्यकता नहीं होती है, तब तक आप System.Collections.Immutableपैकेज का उपयोग कर सकते हैं , अपने फ़ील्ड प्रकार को एक अपरिवर्तनीय संग्रह के रूप में बदल सकते हैं, और फिर सीधे उजागर कर सकते हैं - Fooखुद को अपरिवर्तनीय मानते हैं ।

सीधे सवाल का जवाब देने के लिए अद्यतित उत्तर

क्या किसी IEnumerable के बजाय ReadOnlyCollection के रूप में एक आंतरिक संग्रह को उजागर करने का कोई कारण है यदि कॉलिंग कोड केवल संग्रह पर पुनरावृत्त करता है?

यह इस बात पर निर्भर करता है कि आप कॉलिंग कोड पर कितना भरोसा करते हैं। यदि आप हर उस चीज़ पर पूर्ण नियंत्रण रखते हैं जो कभी इस सदस्य को कॉल करेगी और आप गारंटी देते हैं कि कोई भी कोड कभी भी उपयोग नहीं करेगा:

ICollection<Foo> evil = (ICollection<Foo>) bar.Foos;
evil.Add(...);

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

इसी तरह, जैसा कि आप कहते हैं: यदि आपको केवल आवश्यकता है IEnumerable<T> , तो अपने आप को मजबूत किसी भी चीज़ से क्यों बाँधें?

मूल उत्तर

यदि आप .NET 3.5 का उपयोग कर रहे हैं, तो आप कॉपी बनाने से बच सकते हैं और सरल कॉल को छोड़ कर सरल कास्ट से बच सकते हैं :

public IEnumerable<Foo> Foos {
    get { return foos.Skip(0); }
}

(तुच्छ रूप से लपेटने के लिए बहुत सारे अन्य विकल्प हैं - Skipसेलेक्ट / ओवर के बारे में अच्छी बात यह है कि प्रत्येक पुनरावृत्ति के लिए निरर्थक निष्पादित करने के लिए कोई प्रतिनिधि नहीं है।)

यदि आप .NET 3.5 का उपयोग नहीं कर रहे हैं, तो आप एक ही काम करने के लिए एक बहुत ही सरल आवरण लिख सकते हैं:

public static IEnumerable<T> Wrapper<T>(IEnumerable<T> source)
{
    foreach (T element in source)
    {
        yield return element;
    }
}

15
ध्यान दें कि इसके लिए एक प्रदर्शन दंड है: AFAIK द एनुमरेबल।काउंटर विधि IEnumerables में
डाले गए

6
-1 क्या यह सिर्फ मेरे लिए है या यह एक अलग सवाल का जवाब है? यह इस "समाधान" को जानना उपयोगी है, लेकिन सेशन ने ReadOnlyCollection और IEnumable को वापस करने के बीच तुलना करने के लिए कहा है। यह उत्तर पहले ही मान लेता है कि आप उस निर्णय का समर्थन करने के लिए बिना किसी कारण के IEnumerable को वापस करना चाहते हैं।
ज़ैद मसूद

1
जवाब एक के कास्टिंग ReadOnlyCollectionको दिखाता है ICollectionऔर फिर Addसफलतापूर्वक चल रहा है। जहां तक ​​मुझे पता है, यह संभव नहीं होना चाहिए। evil.Add(...)एक त्रुटि फेंक देना चाहिए। dotnetfiddle.net/GII2jW
शॉन लुटिन

1
@ शौनलुत्तीन: हाँ, बिलकुल - यह फेंकता है। लेकिन मेरे जवाब में, मैं एक कास्ट नहीं करता हूं ReadOnlyCollection- मैंने एक कास्ट किया है IEnumerable<T>जो वास्तव में केवल पढ़ने के लिए नहीं है ... यह उजागर करने के बजाय एक हैReadOnlyCollection
जॉन स्कीट

1
अहा। आपका व्यामोह आपको बुरे कास्टिंग से बचाने के लिए एक के ReadOnlyCollectionबजाय एक का उपयोग करने के लिए नेतृत्व करेगा IEnumerable
शॉन लुटिन

43

यदि आपको केवल संग्रह के माध्यम से पुनरावृति करने की आवश्यकता है:

foreach (Foo f in bar.Foos)

फिर IEnumerable वापस करना पर्याप्त है।

यदि आपको वस्तुओं तक यादृच्छिक पहुँच की आवश्यकता है:

Foo f = bar.Foos[17];

फिर इसे ReadOnlyCollection में लपेटें ।


@Vojislav Stojkovic, +1। क्या यह सलाह आज भी लागू होती है?
w0051977

1
@ w0051977 यह आपके इरादे पर निर्भर करता है। System.Collections.Immutableजॉन स्कीट की सिफारिश के रूप में उपयोग करना पूरी तरह से वैध है जब आप किसी ऐसी चीज को उजागर कर रहे हैं जो आपके संदर्भ में आने के बाद कभी भी बदलने वाली नहीं है। दूसरी ओर, यदि आप एक उत्परिवर्तित संग्रह के केवल-पढ़ने के दृश्य को उजागर कर रहे हैं, तो यह सलाह अभी भी मान्य है, हालांकि मैं शायद इसे IReadOnlyCollection(जो मेरे लिखे जाने पर मौजूद नहीं था) के रूप में उजागर करेगा ।
वोजिस्लाव स्टोजकोविक

@VojislavStojkovic आप के bar.Foos[17]साथ उपयोग नहीं कर सकते IReadOnlyCollection। एकमात्र अंतर अतिरिक्त Countसंपत्ति है। public interface IReadOnlyCollection<out T> : IEnumerable<T>, IEnumerable
कोनराड

@ कोनराड राइट, अगर आपको जरूरत है, तो आपको bar.Foos[17]इसे एक्सपोज करना होगा ReadOnlyCollection<Foo>। यदि आप इसके माध्यम से आकार और पुनरावृति जानना चाहते हैं, तो इसे उजागर करें IReadOnlyCollection<Foo>। यदि आपको आकार जानने की आवश्यकता नहीं है, तो उपयोग करें IEnumerable<Foo>। अन्य समाधान और अन्य विवरण हैं, लेकिन यह इस विशेष उत्तर की चर्चा के दायरे से परे है।
वोजिस्लाव स्टोजकोविक

31

यदि आप ऐसा करते हैं, तो आपके कॉलगर्ल को IEnumerable को ICollection में वापस डालने से रोकना और फिर उसे संशोधित करना कुछ भी नहीं है। ReadOnlyCollection इस संभावना को हटा देता है, हालांकि प्रतिबिंब के माध्यम से अंतर्निहित लिखित संग्रह तक पहुंचना अभी भी संभव है। यदि संग्रह छोटा है, तो इस समस्या को हल करने का एक सुरक्षित और आसान तरीका इसके बजाय एक प्रतिलिपि वापस करना है।


14
यह "पैरानॉयड डिजाइन" की तरह है जिससे मैं दिल से असहमत हूं। मुझे लगता है कि किसी नीति को लागू करने के लिए अपर्याप्त शब्दार्थों को चुनना बुरा है।
वोजिस्लाव स्टोजकोविक

24
आप असहमत हैं यह गलत नहीं है। अगर मैं बाहरी वितरण के लिए एक पुस्तकालय डिजाइन कर रहा हूं, तो मेरे पास उन उपयोगकर्ताओं द्वारा उठाए गए बग से निपटने की तुलना में एक असाधारण इंटरफ़ेस होगा जो एपीआई का दुरुपयोग करने की कोशिश करते हैं। Msdn.microsoft.com/en-us/library/k2604h5s(VS.71).aspx
Stu Mackellar

5
हां, बाहरी वितरण के लिए एक पुस्तकालय डिजाइन करने के विशिष्ट मामले में, यह जो भी आप उजागर कर रहे हैं, उसके लिए एक व्यंग्यात्मक इंटरफ़ेस है। फिर भी, यदि फोस संपत्ति का शब्दार्थ अनुक्रमिक पहुंच है, तो ReadOnlyCollection का उपयोग करें और फिर IEnumable को वापस करें। गलत शब्दार्थ का उपयोग करने की आवश्यकता नहीं है;)
वोजिस्लाव स्टोजकोविक

9
आपको या तो करने की आवश्यकता नहीं है। आप प्रतिलिपि के बिना केवल पढ़ने के लिए पुनरावृत्त कर सकते हैं ...
जॉन स्कीट

1
@shojtsy आपकी कोड ऑफ़ लाइन काम नहीं करती है। जैसा कि एक अशक्त उत्पन्न करता है । एक कलाकार एक अपवाद फेंकता है।
निक अलेक्सीव

3

मैं जितना संभव हो ReadOnlyCollection का उपयोग करने से बचता हूं, यह वास्तव में सामान्य सूची का उपयोग करने की तुलना में काफी धीमा है। इस उदाहरण को देखें:

List<int> intList = new List<int>();
        //Use a ReadOnlyCollection around the List
        System.Collections.ObjectModel.ReadOnlyCollection<int> mValue = new System.Collections.ObjectModel.ReadOnlyCollection<int>(intList);

        for (int i = 0; i < 100000000; i++)
        {
            intList.Add(i);
        }
        long result = 0;

        //Use normal foreach on the ReadOnlyCollection
        TimeSpan lStart = new TimeSpan(System.DateTime.Now.Ticks);
        foreach (int i in mValue)
            result += i;
        TimeSpan lEnd = new TimeSpan(System.DateTime.Now.Ticks);
        MessageBox.Show("Speed(ms): " + (lEnd.TotalMilliseconds - lStart.TotalMilliseconds).ToString());
        MessageBox.Show("Result: " + result.ToString());

        //use <list>.ForEach
        lStart = new TimeSpan(System.DateTime.Now.Ticks);
        result = 0;
        intList.ForEach(delegate(int i) { result += i; });
        lEnd = new TimeSpan(System.DateTime.Now.Ticks);
        MessageBox.Show("Speed(ms): " + (lEnd.TotalMilliseconds - lStart.TotalMilliseconds).ToString());
        MessageBox.Show("Result: " + result.ToString());

15
समयपूर्व अनुकूलन। मेरे माप से, ReadOnlyCollection से अधिक पुनरावृति एक नियमित सूची की तुलना में लगभग 36% अधिक समय लेती है, यह मानते हुए कि आप लूप के लिए कुछ भी नहीं करते हैं । संभावना है, आप इस अंतर को कभी नोटिस नहीं करेंगे, लेकिन अगर यह आपके कोड में एक हॉट-स्पॉट है और आपको हर अंतिम प्रदर्शन की आवश्यकता है, तो आप इसके बजाय एक एरे का उपयोग क्यों कर सकते हैं? यह अभी और तेज होगा।
स्ट्रिपलिंगवर्यर

आपके बेंचमार्क में खामियां हैं, आप पूरी तरह से शाखा की भविष्यवाणी की अनदेखी कर रहे हैं।
Trojaner

0

कभी-कभी आप एक इंटरफ़ेस का उपयोग करना चाह सकते हैं, शायद इसलिए कि आप यूनिट परीक्षण के दौरान संग्रह का मजाक बनाना चाहते हैं। कृपया एडेप्टर का उपयोग करके अपने स्वयं के इंटरफ़ेस को ReadonlyCollection में जोड़ने के लिए मेरा ब्लॉग प्रविष्टि देखें ।

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