2009-08-25 10 views
12

मुझे पता है कि मुझे किसी संपत्ति में List<T> का खुलासा नहीं करना चाहिए, लेकिन मुझे आश्चर्य है कि ऐसा करने का उचित तरीका क्या है? उदाहरण के लिए, यह कर:उचित रूप से एक सूची का खुलासा <T>?

public static class Class1 
{ 
    private readonly static List<string> _list; 

    public static IEnumerable<string> List 
    { 
     get 
     { 
      return _list; 
      //return _list.AsEnumerable<string>(); behaves the same 
     } 
    } 

    static Class1() 
    { 
     _list = new List<string>(); 
     _list.Add("One"); 
     _list.Add("Two"); 
     _list.Add("Three"); 
    } 
} 

मेरे फोन करने वाले बस List<T> वापस करने के लिए कास्ट करने के लिए अनुमति होगी:

private void button1_Click(object sender, EventArgs e) 
    { 
     var test = Class1.List as List<string>; 
     test.Add("Four"); // This really modifies Class1._list, which is bad™ 
    } 

तो अगर मैं एक बहुत अपरिवर्तनीय List<T> चाहते मैं हमेशा एक नई सूची बनाने करना होगा? उदाहरण के लिए, यह काम करने लगता है (परीक्षण कलाकारों के बाद रिक्त है):

public static IEnumerable<string> List 
    { 
     get 
     { 
      return new ReadOnlyCollection<string>(_list); 
     } 
    } 

लेकिन मैं चिंतित हूँ अगर वहाँ एक प्रदर्शन भूमि के ऊपर के रूप में मेरी सूची में हर बार जब कोई इसे उपयोग करने की कोशिश करता है क्लोन है?

+2

क्या गलत डब्ल्यू एक संपत्ति के रूप में (टी की) एक सूची उजागर/है? – Jason

+0

आप इसे विस्तारित नहीं कर सकते: http://blogs.msdn.com/fxcop/archive/2006/04/27/faq-why-does-donotexposegenericlists-recommend-that-i-expose-collection-lt-t-gt- इसके बजाय-सूची-lt-t-gt-david-kean.aspx –

+0

मुझे समझ में नहीं आता कि इसका क्या मतलब है? मेरा मतलब है, मैं सिर्फ संपत्ति के रूप में संपत्ति का पर्दाफाश करूँगा ... ??? क्या यह एक थ्रेड-सुरक्षित मुद्दा है? –

उत्तर

2
+1

'AsReadOnly' केवल' ReadOnlyCollection' के कन्स्ट्रक्टर को कॉल करता है, जैसा कि प्रश्न में उदाहरण कोड के समान है। – LukeH

+1

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

+1

हेनरिक और मार्क के जवाब के बीच फाड़ा गया था। इसे स्वीकार करते हुए क्योंकि मुझे AsReadOnly() के बारे में पता नहीं था, क्योंकि यह बहुत संक्षिप्त है, क्योंकि यह समस्या हल करता है, क्योंकि मैं इसे कैश कर सकता हूं (एक निजी पठनीय स्थिर रीडऑनली कोलेक्शन _listWrapper; इसे कन्स्ट्रक्टर में स्थापित करना, और फिर इसे वापस करना), और ऐसा लगता है कि ऐसा करने का उचित तरीका है (मैंने एरिक लिपर्ट को इसे लपेटने का एक अच्छा तरीका बताया है)। थ्रेड सुरक्षा एक मुद्दा हो सकता है, लेकिन यह वैसे भी एक सामान्य मुद्दा है। –

4

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

7

एक List<T> उजागर के रूप में एक संपत्ति वास्तव में सभी बुराई की जड़ नहीं है, खासकर यदि यह foo.Items.Add(...) जैसे अपेक्षित उपयोग की अनुमति देता है।

आप AsEnumerable() करने के लिए एक कच्चे सुरक्षित विकल्प लिख सकते हैं:

public static IEnumerable<T> AsSafeEnumerable<T>(this IEnumerable<T> data) { 
    foreach(T item in data) yield return item; 
} 

लेकिन इस समय अपने सबसे बड़ी समस्या यह धागा सुरक्षा है। एक स्थिर सदस्य के रूप में, आपको यहां बड़ी समस्याएं हो सकती हैं, खासकर यदि यह एएसपी.नेट जैसी चीज़ में है। यहां तक ​​कि ReadOnlyCollection किसी मौजूदा सूची से अधिक इस से ग्रस्त होगा:

 List<int> ints = new List<int> { 1, 2, 3 }; 
     var ro = ints.AsReadOnly(); 
     Console.WriteLine(ro.Count); // 3 
     ints.Add(4); 
     Console.WriteLine(ro.Count); // 4 

तो बस AsReadOnly साथ लपेटकर है नहीं अपने वस्तु धागा सुरक्षित बनाने के लिए पर्याप्त; यह केवल उपभोक्ता डेटा जोड़ने के खिलाफ सुरक्षा करता है (लेकिन जब भी आप सिंक्रनाइज़ या प्रतिलिपि बनाते हैं, तब भी वे डेटा को जोड़ते समय भी इसका अनुमान लगा सकते हैं)।

+0

एस/बाहर निकलने/मौजूदा /: पी (लेकिन +1!) –

+0

मैं हमेशा भूल जाता हूं कि उपज मौजूद है ... एएसपीनेट में थ्रेड सुरक्षा इतना मुद्दा नहीं है क्योंकि मैं एएसपी में परिवर्तनीय स्थैतिक कक्षाओं का उपयोग नहीं करने की कोशिश करता हूं। नेट क्योंकि वे अनुरोधों में साझा किए जाते हैं। मुझे लगता है कि अगर मुझे "स्नैपशॉट" चाहिए, तो किसी भी तरह की प्रतिलिपि के आसपास कोई रास्ता नहीं है, इसलिए 'नई सूची (_list) वापस करें;' 'हल' होगा, और यदि कॉलर इसे वापस सूची में रखता है तो वह केवल संशोधित करता है अपनी खुद की प्रतिलिपि लेकिन मेरा नहीं। मुख्य बिंदु वास्तव में क्लास 1._लिस्ट 'अपरिवर्तनीय' को बाहरी कॉलर्स में रखना और कक्षा 1 में कार्यों को जोड़ें/निकालना है। –

+1

हां, पूरी प्रति सूची में किसी ऑब्जेक्ट्स ** के गुणों को बदलने के अलावा पूरी प्रतिलिपि सभी बुराइयों के खिलाफ सुरक्षा करती है। –

2

आपको क्लोनिंग के ऊपरी हिस्से के बारे में चिंता करने की आवश्यकता नहीं है: ReadOnlyCollection के साथ संग्रह को लपेटना क्लोन करें। यह सिर्फ एक रैपर बनाता है; यदि अंतर्निहित संग्रह बदलता है, तो पाठक संस्करण भी बदल जाता है।

यदि आप बार-बार ताजा रैपर बनाने की चिंता करते हैं, तो आप इसे एक अलग आवृत्ति चर में कैश कर सकते हैं।

3

यदि कक्षा का कोई अन्य उद्देश्य नहीं है तो आप सूची से प्राप्त कर सकते हैं और ऐड विधि को ओवरराइड कर सकते हैं और इसे अपवाद फेंक सकते हैं।

+1

आपको 'संग्रह ' से उत्तराधिकारी होना होगा ऐसा करें ... 'सूची ' में 'वर्चुअल' सदस्य नहीं हैं। –

+0

लेकिन फिर यह एक एलएसपी उल्लंघन होगा, लेकिन फिर भी एक दिलचस्प 'हैक विचार'। –

+0

यह एक विकल्प हो सकता है, यह सिर्फ गलत लगता है क्योंकि मुझे लगता है कि फ्रेमवर्क पहले से ही बॉक्स के बाहर एक उपयुक्त तंत्र प्रदान करना चाहिए। –

0

असीमित और ReadOnlyCollection में समस्या है जब आपकी गणना मिडवे पर है और संग्रह संशोधित हो जाता है। ये चीजें धागे सुरक्षित नहीं हैं। उन्हें एक सरणी के रूप में लौटाना और कॉलिंग के समय उन्हें कैशिंग करना बेहतर विकल्प हो सकता है।

उदाहरण के लिए,

public static String[] List{ 
    get{ 
     return _List.ToArray(); 
    } 
} 

//While using ... 

String[] values = Class1.List; 

foreach(string v in values){ 
    ... 
} 

// instead of calling foreach(string v in Class1.List) 
// again and again, values in this context will not be 
// duplicated, however values are cached instance so 
// immediate changes will not be available, but its 
// thread safe 
foreach(string v in values){ 
    ... 
} 
+0

सार्वजनिक वर्ग अनुबंध में Arrays को खराब अभ्यास के रूप में माना जाता है – Przemaas

+0

हमेशा नहीं, लेकिन एरिक लिपर्ट के बारे में कुछ विचार थे: http://blogs.msdn.com/ericlippert/archive/2008/09/22/arrays-Considered- कुछ हद तक- हानिकारक.एएसपीएक्स –

+0

@ प्रेजेमास, कोई सामान्य नियम नहीं है जैसे कि एरे खराब अभ्यास हैं, इसकी हमेशा स्थिति निर्भर होती है, अगर आप सूची के स्रोत को अलग करते हैं और देखते हैं, तो इसकी अपनी वस्तुओं को भी स्टोर करने के लिए एक सरणी है। जब आप सामग्री को संशोधित नहीं करना चाहते हैं, और आपको केवल संग्रह को पढ़ने की आवश्यकता है, तो सरणी इसे करने का एकमात्र सबसे तेज़ तरीका है। –

1

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

+0

यह सच है, और जैसा कि स्विस ने इंगित किया है कि यह (पास?) डेटा को गहराई से कॉपी किए बिना वैसे भी असंभव है, लेकिन मैं लोगों को पैर में आसानी से शूटिंग करने से रोकने के लिए थोड़ा बाधा रखना चाहता था। –

2

मैं एक ऐसी ही सवाल पहले से पूछा:

उस आधार पर मैं सुझाव है कि आप List<T> आंतरिक रूप से उपयोग करते हैं, और एक Collection<T> या IList<T> के रूप में इसे वापस। या अगर यह केवल IEnumerable<T> की गणना करने या जोड़ने के लिए जरूरी है और नहीं।

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

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