2011-01-23 19 views
15

इससे पहले कि मैं भी पूछते हैं, मुझे रास्ते से हट स्पष्ट जवाब मिलता है: ICollection<T> इंटरफ़ेस एक मनमाना तत्व दूर करने के लिए है, जो Queue<T> और Stack<T> वास्तव में (समर्थन नहीं कर सकते हैं क्योंकि वे केवल "अंत" निकाल सकते हैं एक Remove विधि शामिल तत्व)।क्यूई (टी) और स्टैक (टी) क्यों आईसीओलेक्शन (टी) लागू नहीं करते हैं?

ठीक है, मुझे एहसास है। असल में, मेरा प्रश्न विशेष रूप से Queue<T> या Stack<T> संग्रह प्रकारों के बारे में नहीं है; बल्कि, जेनेरिक प्रकार के लिए T मानों का संग्रह अनिवार्य रूप से ICollection<T> लागू करने के डिजाइन निर्णय के बारे में है।

यहां मुझे अजीब लगता है। मान लें कि मेरे पास एक विधि है जो T के मनमानी संग्रह को स्वीकार करती है, और जिस कोड को मैं लिख रहा हूं उसके उद्देश्य के लिए संग्रह के आकार को जानना उपयोगी होगा। उदाहरण के लिए (नीचे दिए गए कोड उदाहरण के लिए ही तुच्छ है,!):

// Argument validation omitted for brevity. 
static IEnumerable<T> FirstHalf<T>(this ICollection<T> source) 
{ 
    int i = 0; 
    foreach (T item in source) 
    { 
     yield return item; 
     if ((++i) >= (source.Count/2)) 
     { 
      break; 
     } 
    } 
} 

अब, वहाँ वास्तव में कोई कारण नहीं क्यों इस कोड को एक Queue<T> या एक Stack<T> पर काम नहीं कर सकता है, सिवाय इसके कि उन प्रकार को लागू नहीं करते ICollection<T>। वेICollection लागू जाहिर है-मैं Count संपत्ति के लिए मुख्य रूप से अनुमान लगा कर अकेले-लेकिन यह है कि इस तरह अजीब अनुकूलन कोड की ओर जाता है:

// OK, so to accommodate those bastard Queue<T> and Stack<T> types, 
// we will just accept any IEnumerable<T>... 
static IEnumerable<T> FirstHalf<T>(this IEnumerable<T> source) 
{ 
    int count = CountQuickly<T>(source); 
    /* ... */ 
} 

// Then, assuming we've got a collection type with a Count property, 
// we'll use that... 
static int CountQuickly<T>(IEnumerable collection) 
{ 
    // Note: I realize this is basically what Enumerable.Count already does 
    // (minus the exception); I am just including it for clarity. 
    var genericColl = collection as ICollection<T>; 
    if (genericColl != null) 
    { 
     return genericColl.Count; 
    } 

    var nonGenericColl = collection as ICollection; 
    if (nonGenericColl != null) 
    { 
     return nonGenericColl.Count; 
    } 

    // ...or else we'll just throw an exception, since this collection 
    // can't be counted quickly. 
    throw new ArgumentException("Cannot count this collection quickly!"); 
} 

इसे और अधिक समझ बनाने के सिर्फ परित्याग करने के लिए नहीं चाहेंगे ICollection इंटरफ़ेस पूरी तरह से (मेरा मतलब यह नहीं है कि कार्यान्वयन ड्रॉप, निश्चित रूप से, क्योंकि यह एक तोड़ने वाला परिवर्तन होगा; मेरा मतलब है, इसका उपयोग करना बंद करें), और केवल उन सदस्यों के लिए स्पष्ट कार्यान्वयन के साथ ICollection<T> लागू करें जिनके पास सही नहीं है मेल खाते हैं?

मेरा मतलब है, क्या ICollection<T> प्रस्तावों को देखो:

  • Count - Queue<T> और Stack<T> दोनों इस की है।
  • IsReadOnly - Queue<T> और Stack<T> आसानी से यह हो सकता है।
  • Add - Queue<T>, (Enqueue के साथ) स्पष्ट रूप से यह लागू हो सकता है कर सकते थे Stack<T> (Push के साथ) के रूप में।
  • Clear - जांचें।
  • Contains - जांचें।
  • CopyTo - जांचें।
  • GetEnumerator - चेक (डुह)।
  • Remove - यह केवल एकमात्र ऐसा है जो Queue<T> और Stack<T> के लिए एकदम सही मिलान नहीं है।

और यहाँ असली किकर है: ICollection<T>.Removeरिटर्न एक bool; इसलिए Queue<T> के लिए एक स्पष्ट कार्यान्वयन पूरी तरह से (उदाहरण के लिए) की जांच कर सकता है, तो आइटम हटा दिया जाना चाहिए वास्तव में सिर तत्व (Peek का उपयोग) है, और यदि हां, Dequeue फोन और true लौटने के लिए, अन्यथा false लौट आते हैं। Stack<T> को Peek और Pop के साथ आसानी से एक ही कार्यान्वयन दिया जा सकता है। क्यों नहीं किया Queue<T> और Stack<T> के डिजाइनरों इस इंटरफ़ेस को लागू:

ठीक है, अब है कि मैं क्यों मैं लगता है कि यह संभव हो जाएगा पर एक हजार शब्दों के बारे में लिखा है, मैं स्पष्ट सवाल मुद्रा ? यही है, डिजाइन कारक क्या थे (जिन्हें मैं शायद विचार नहीं कर रहा हूं) जिससे निर्णय हुआ कि यह गलत विकल्प होगा? ICollection क्यों लागू किया गया था?

मुझे आश्चर्य है कि, मेरे अपने प्रकारों को डिजाइन करने में, क्या कोई मार्गदर्शक सिद्धांत है जो मुझे इंटरफेस कार्यान्वयन के संबंध में विचार करना चाहिए जिसे मैं इस प्रश्न पूछने में अनदेखा कर सकता हूं। उदाहरण के लिए, क्या यह केवल उन इंटरफेस को स्पष्ट रूप से कार्यान्वित करने के लिए बुरी आदत माना जाता है जो सामान्य रूप से पूरी तरह से समर्थित नहीं हैं (यदि हां, तो List<T>IList लागू करने के साथ ऐसा लगता है)? क्या कोई वैचारिक कतार/स्टैक की अवधारणा के बीच डिस्कनेक्ट है और ICollection<T> का प्रतिनिधित्व करने के लिए क्या है?

मूल रूप से, मैं समझ होनी चाहिए कि एक बहुत अच्छा कारण Queue<T> (उदाहरण के लिए) नहीं लागू ICollection<T>, और मैं बस आँख बंद करके आगे जाने के लिए अगर कोई अनुचित सामग्री में अपने ही प्रकार के इंटरफेस डिजाइन और लागू करने नहीं करना चाहते मैं जो कुछ भी कर रहा हूं उसके माध्यम से सूचित किए बिना पूरी तरह से सोचने के तरीके।

मैं सुपर-लंबे प्रश्न के लिए क्षमा चाहता हूं।

+4

बस एमएस के खराब संग्रह पुस्तकालयों का एक और उदाहरण। इंटरफ़ेस को केवल पढ़ने-पढ़ने और पढ़ने-लिखने वाले संस्करणों में पढ़ने के लिए कम से कम इस समस्या को हल किया जा सकता था (केवल पढ़ने-योग्य संस्करण से प्राप्त पढ़ने वाले संस्करण के साथ) और यहां तक ​​कि बेहतर अनाज वाले इंटरफ़ेस भी होते हैं, केवल एक पर ध्यान केंद्रित करते हैं अप्रासंगिक गुणों और विधियों को शामिल करने के बजाय संग्रह का विशिष्ट पहलू। तो ICollection में शामिल, साफ़ या निकालना शामिल नहीं होना चाहिए था। यह छोड़ा जाना चाहिए था, उदाहरण के लिए, IMutableCollection । आईसीओलेक्शन अधिक कक्षाओं द्वारा कार्यान्वित किया जा सकता है। – siride

+0

@ साइराइड: एमएस संग्रह पुस्तकालयों का आपका विचार मेरा दर्पण करता है। यह अजीब बात है कि अनुक्रमित गुणों जैसी चीजों को केवल पढ़ने के लिए और पढ़ने-लिखने के संस्करणों के लिए दो बार परिभाषित किया जाना चाहिए, लेकिन ऐसा जीवन है।'12 निकालें 'को लागू करने के लिए नवाचार के लिए – supercat

+0

+1, यह अच्छा था :) और नहीं, यह सुपर-लंबा क्यू अच्छी तरह लिखा गया है। – nawfal

उत्तर

5

मैं "वास्तविक सोच क्या था" जवाब नहीं दे सकता - शायद डिजाइनरों में से एक हमें वास्तविक Thinkng दे देगा और मैं इसे हटा सकता हूं।

हालांकि, अपने आप की मानसिकता में डाल "क्या अगर किसी को मेरे पास आया यह फैसला लेने के लिए", मैं एक जवाब के बारे में सोच सकते हैं .. मुझे इस कोड के साथ वर्णन करते हैं:

public void SomeMethod<T>(ICollection<T> collection, T valueA, T valueB) 
{ 

    collection.Add(valueA); 
    collection.Add(valueB); 

    if(someComplicatedCondition()) 
    { 
    collection.Remove(valueA); 
    } 
} 

(बेशक, कोई भी ICollection<T> का खराब कार्यान्वयन कर सकता है, लेकिन हम फ्रेमवर्क को उदाहरण सेट करने की उम्मीद करते हैं)। चलिए मानते हैं कि स्टैक/कतार लागू होता है जैसा कि आप सवाल में बताते हैं। तो क्या उपरोक्त कोड सही है, या इसमें एज केस बग हैं क्योंकि ICollection<T>.Remove() चेक किया जाना चाहिए? यदि valueA हटा दिया जाना चाहिए, तो मैं इसे स्टैक और कतार दोनों के साथ काम करने के लिए कैसे ठीक करूं? जवाब हैं, लेकिन स्पष्ट रूप से उपरोक्त कोड इस मामले में गलत है - भले ही यह उचित गंध करता है।

तो दोनों व्याख्याएं वैध हैं, लेकिन मैं यहां किए गए निर्णय के साथ अच्छा हूं - अगर मेरे पास उपरोक्त कोड था और मुझे पता था कि मुझे एक कतार या स्टैक पारित किया जा सकता है जिसे मैं इसके आसपास डिजाइन कर सकता हूं, लेकिन यह निश्चित रूप से एक होगा आसान बग गड्ढे में गिर (हर जगह आप ICollection<T> देखते हैं, निकालने के लिए किनारे मामलों याद!)

+0

मुझे लगता है कि यह एक उत्कृष्ट उदाहरण है जो यहां तर्क को बहुत ठोस बनाता है। मुझे इस के चारों ओर अपना सिर लेने में मदद करने के लिए धन्यवाद। आगे बढ़ते हुए, मैं खुद को उन उदाहरणों के साथ चुनौती देने के लिए चुनौती दूंगा जहां एक निश्चित इंटरफेस को लागू करने के लिए "अपूर्ण" अत्यधिक अप्रत्याशित व्यवहार होगा- ऐसे मामलों में, मैं उन इंटरफेस को करने से पहले लंबे और कठिन सोचूंगा। उदाहरण सेट करने के लिए * ढांचे के लिए –

+0

+1 *। अच्छे से कहा। – nawfal

1

आखिरकार वे आदर्श आदर्श नहीं हैं; - अगर आप किसी सूची या संग्रह चाहते हैं एक List<T> या Collection<T> का उपयोग करें; पी

पुन Add - वहाँ एक अंतर्निहित धारणा है कि Add संग्रह है, जो एक ढेर के सच नहीं है की अंत में जोड़ता है (हालांकि यह है एक कतार के लिए)। कुछ मायनों में, यह वास्तव में मुझे भ्रमित करता है कि स्टैक/कतार के लिए गणनाकर्ता डेक्यू/पॉप नहीं है, क्योंकि मैं बड़े पैमाने पर प्रत्येक कतार/स्टैक में वस्तुओं को प्रत्येक बार (और केवल एक बार) लाने की अपेक्षा करता हूं। एक

हो सकता है कि यह भी (फिर से, बस एक उदाहरण के रूप में मेरे प्रगणक का प्रयोग करके) बस लोगों को वास्तव में पर सहमत नहीं हो सकता है कि कैसे यह उन परिदृश्यों में से कुछ से व्यवहार करना चाहिए, और पूर्ण समझौते की कमी है, बस उन्हें लागू नहीं था बेहतर विकल्प

+0

@Marc: कोई समस्या नहीं; लोग हर दो बार मिश्रण करते हैं (मुझे पता है कि मेरे पास है)। मुझे पता है कि कतार/ढेर पर गणना करने के बारे में आपका क्या मतलब है; जो कभी भी कतार को गिनती नहीं कर लेता है, वास्तव में इसे छोड़ने के बिना? मुझे लगता है कि समस्या का हिस्सा यह है कि 'आईन्यूमेरेटर (टी)' के लिए प्रलेखन कहता है: "संग्रह में डेटा को पढ़ने के लिए गणनाकर्ताओं का उपयोग किया जा सकता है, लेकिन इन्हें अंतर्निहित संग्रह को संशोधित करने के लिए उपयोग नहीं किया जा सकता है।" खुद को एक कोने में रखने की तरह। –

+0

@ दैन ताओ: समाधान में आईएसटाक और आईक्यूयू लागू नहीं होगा, लेकिन इसके बजाय उन्हें डेक्यूएल या पॉपएल विधियां प्रदान की जाएंगी जो एक आईनेमरेबल लौटाएंगी। ध्यान दें कि थ्रेड-सुरक्षित आईएसटीक के लिए पॉपएल विधि स्टैक से सब कुछ मैन्युअल रूप से पॉप करने से अलग-अलग परिणाम प्राप्त कर सकती है, क्योंकि थ्रेड-सुरक्षित पॉपएल को यह गारंटी देनी चाहिए कि पॉपएल के समय के आसपास धक्का दिया गया कोई आइटम या तो सबसे ऊपर आइटम लौटाया जाएगा या अन्यथा वापस नहीं किया जा सकता है लेकिन ढेर में रहते हैं; 'पॉप' संचालन के मैनुअल अनुक्रम में ऐसी कोई गारंटी नहीं होगी। – supercat

+0

@supercat: मैं मानता हूं, 'IEnumerable ' कार्यान्वयन के बिना इस व्यवहार को पूरा करने के निश्चित तरीके हैं; उस ने कहा, मैं सहमत नहीं हूं कि 'कतार ' और 'स्टैक '* * लागू नहीं होना चाहिए' IENumerable '। यह एक दुर्लभ मामले की तरह लगता है जहां आप हटाने के बिना गणना करना चाहते हैं। व्यक्तिगत रूप से, मैं एक्सटेंशन विधियों का उपयोग करता हूं जो मूल रूप से आपके 'डेक्यूएल' और 'पॉपएल' सुझावों के समान कार्य करते हैं। –

0

फिलिप एक अच्छा जवाब (+1) दे दिया है। एक और वैचारिक वादा है कि RemoveStack<T> के लिए तोड़ देगा।

पहले ICollection से एक विशिष्ट वस्तु की घटना को निकालता है: के रूप में ICollection<T>.Remove प्रलेखित है।

Stack<T>, LIFO है, भले ही Remove लागू किया गया था, यह नकली वस्तुओं के मामले में अंतिम बार होने को दूर करना होगा।

यह does not Stack<T> के लिए कोई मतलब है, यह बेहतर इसके बराबर और विपरीत चचेरे भाई के लिए परिहार्य है।


मैं इसे बेहतर अगर एमएस पसंद किया है जाएगा:

  • ICollection<T> उस तरह के लिए Remove दस्तावेज़ नहीं किया। कहीं भी एक समान वस्तु को हटाने से अधिक समझदारी होगी कि विभिन्न संरचनाओं के आंतरिक कार्यान्वयन कितने अलग हैं। पहली वस्तु को हटाने के लिए मजबूर करना जैसे सरणी जैसे सरल संरचनाओं की रैखिक खोज से प्रभावित होता है।

  • में कतार संरचनाओं के लिए इंटरफेस थे। हो सकता है:

    public interface IPeekable<T> : IEnumerable<T> // or IInOut<T> or similar 
    { 
        int Count { get; } 
    
        bool Contains(T item); 
        T Peek(); 
        void Add(T item); 
        bool Remove(); 
        void Clear(); 
    } 
    
संबंधित मुद्दे