एक संग्रह में AddRange


109

एक सहकर्मी ने आज मुझसे पूछा कि एक संग्रह में एक सीमा कैसे जोड़नी है। उसके पास एक वर्ग है जो विरासत में मिला है Collection<T>। वहाँ केवल उस प्रकार की एक संपत्ति है जिसमें पहले से ही कुछ आइटम हैं। वह किसी अन्य संग्रह में आइटम को संपत्ति संग्रह में जोड़ना चाहता है। वह सी # 3-फ्रेंडली अंदाज़ में ऐसा कैसे कर सकता है? (केवल-संपत्ति के बारे में बाधा पर ध्यान दें, जो संघ और पुनर्मुद्रण करने जैसे समाधानों को रोकता है।)

ज़रूर, संपत्ति के साथ एक foreach। जोड़ काम करेगा। लेकिन एक List<T>शैली AddRange अधिक सुंदर होगा।

एक्सटेंशन विधि लिखना आसान है:

public static class CollectionHelpers
{
    public static void AddRange<T>(this ICollection<T> destination,
                                   IEnumerable<T> source)
    {
        foreach (T item in source)
        {
            destination.Add(item);
        }
    }
}

लेकिन मुझे लग रहा है कि मैं पहिया को फिर से मजबूत कर रहा हूं। मैं System.Linqया morelinq में समान कुछ भी नहीं मिला ।

खराब डिजाइन? बस कॉल जोड़ें? स्पष्ट याद आ रही है?


5
याद रखें कि LINQ से Q 'क्वेरी' है और वास्तव में डेटा पुनर्प्राप्ति, प्रक्षेपण, परिवर्तन आदि के बारे में है, मौजूदा संग्रह को संशोधित करना वास्तव में LINQ के इच्छित उद्देश्य के दायरे में नहीं आता है, यही कारण है कि LINQ कुछ भी प्रदान नहीं करता है- इसके लिए लेकिन विस्तार के तरीके (और विशेष रूप से आपका नमूना) इसके लिए आदर्श होगा।
लेवी

एक समस्या, ICollection<T>एक Addविधि नहीं है। msdn.microsoft.com/en-us/library/… हालाँकि Collection<T>एक है।
टिम गुडमैन

@TimGoodman - यह गैर-सामान्य इंटरफ़ेस है। देखें msdn.microsoft.com/en-us/library/92t2ye13.aspx
TrueWill

"मौजूदा संग्रह को संशोधित करना वास्तव में LINQ के इच्छित उद्देश्य के दायरे में नहीं आता है"। @ लेवी फिर भी Add(T item)पहले स्थान पर क्यों है? एक एकल आइटम जोड़ने की क्षमता की पेशकश करने के लिए एक आधे-बेक्ड दृष्टिकोण की तरह लगता है और फिर सभी कॉलर्स को एक समय में एक से अधिक जोड़ने के लिए पुनरावृति की उम्मीद है। आपका कथन निश्चित रूप से सही है, IEnumerable<T>लेकिन मैंने ICollectionsएक से अधिक अवसरों पर खुद को निराश पाया है । मैं आपसे असहमत नहीं हूं, बस वेंटिंग है।
अकस्मात

जवाबों:


62

नहीं, यह पूरी तरह से उचित लगता है। एक List<T>.AddRange () विधि है जो मूल रूप से सिर्फ यह करता है, लेकिन आपके संग्रह को ठोस बनाने की आवश्यकता है List<T>


1
धन्यवाद; बहुत सही है, लेकिन अधिकांश सार्वजनिक संपत्तियां एमएस के दिशानिर्देशों का पालन करती हैं और सूची में नहीं हैं।
ट्रूविल

7
हाँ - मैं इसे औचित्य के रूप में दे रहा था क्योंकि मुझे नहीं लगता कि ऐसा करने में कोई समस्या है। बस यह महसूस करें कि यह सूची <T> संस्करण की तुलना में कम कुशल होगा (चूंकि सूची <T> पूर्व-आवंटित कर सकता है)
रीड कोपसे

बस ध्यान रखें कि .NET कोर 2.2 में AddRange पद्धति गलत तरीके से उपयोग किए जाने पर एक अजीब व्यवहार दिखा सकती है, जैसा कि इस अंक में दिखाया गया है: github.com/dotnet/core/issues/2667
ब्रूनो

36

लूप चलाने से पहले एक्सटेंशन विधि में सूची के लिए कास्टिंग का प्रयास करें। इस तरह आप List.AddRange के प्रदर्शन का लाभ उठा सकते हैं।

public static void AddRange<T>(this ICollection<T> destination,
                               IEnumerable<T> source)
{
    List<T> list = destination as List<T>;

    if (list != null)
    {
        list.AddRange(source);
    }
    else
    {
        foreach (T item in source)
        {
            destination.Add(item);
        }
    }
}

2
asऑपरेटर फेंक कभी नहीं होगा। यदि destinationडाली नहीं जा सकती है, listतो अशक्त होगी और elseब्लॉक निष्पादित करेगा।
rymdsmurf

4
arrgggh! सभी पवित्र है कि प्यार के लिए हालत शाखाओं स्वैप!
निकोडेमस 13

13
मैं वास्तव में गंभीर हूं। मुख्य कारण यह है कि यह अतिरिक्त संज्ञानात्मक-भार है, जो अक्सर वास्तव में काफी कठिन होता है। आप लगातार नकारात्मक परिस्थितियों का मूल्यांकन करने की कोशिश कर रहे हैं, जो आमतौर पर अपेक्षाकृत कठिन होता है, आपकी दोनों शाखाएं वैसे भी होती हैं, यह (IMO) यह कहना आसान है कि यदि 'शून्य' ऐसा करते हैं, तो इसके विपरीत इसके बजाय ऐसा करते हैं। यह चूक के बारे में भी है, उन्हें जितनी बार संभव हो सकारात्मक अवधारणा होनी चाहिए। (`if ((बात ।Disabled) {} ​​और {} 'आपको रोकने की आवश्यकता है और लगता है कि' आह, नहीं है अक्षम साधन सक्षम है, ठीक है, मिल गया है, इसलिए दूसरी शाखा है जब यह आईएस अक्षम है)। पार्स करना मुश्किल है।
निकोडेमस 13

13
"कुछ! = अशक्त" की व्याख्या करना "कुछ == अशक्त" की व्याख्या करने से अधिक कठिन नहीं है। नकारात्मक ऑपरेटर हालांकि एक पूरी तरह से अलग चीज है, और आपके अंतिम उदाहरण में, अगर-और-स्टेटमेंट को फिर से लिखना है तो उस ऑपरेटर को हटा देगा । यह एक उद्देश्यपूर्ण सुधार है, लेकिन एक जो मूल प्रश्न से संबंधित नहीं है। उस विशेष मामले में दो रूप व्यक्तिगत प्राथमिकताओं का विषय हैं, और मैं ऊपर दिए गए तर्क को देखते हुए "! =" ऑपरेटर को प्राथमिकता दूंगा।
rymdsmurf

15
पैटर्न मिलान सभी को खुश कर देगा ... ;-)if (destination is List<T> list)
जैकब फ़ॉशी

28

के बाद से .NET4.5यदि आप एक लाइनर चाहते आप उपयोग कर सकते हैं System.Collections.Genericforeach।

source.ForEach(o => destination.Add(o));

या उससे भी कम

source.ForEach(destination.Add);

प्रदर्शन-वार यह प्रत्येक लूप (सिंटैक्टिक शुगर) के समान है।

इसके अलावा इसे असाइन करने का प्रयास करें

var x = source.ForEach(destination.Add) 

कारण ForEachशून्य है।

संपादित करें: टिप्पणियों से कॉपी किया गया, फोर्च पर लिप्ट की राय


9
व्यक्तिगत रूप से मैं इस पर लिप्टर्ट के
TrueWill

1
क्या यह स्रोत होना चाहिए। फ़ोरएच (गंतव्य। एड)?
फ्रैंक

4
ForEachलगता है केवल पर परिभाषित किया जा सकता है List<T>, नहीं Collection?
रक्षक एक


एरिक लिपर्ट के ब्लॉग पोस्ट की अपडेट की गई कड़ी: कोडिंग में शानदार रोमांच | "फॉरआच" बनाम "फॉरएच"
अलेक्जेंडर

19

याद रखें कि प्रत्येक Addसंग्रह की क्षमता की जांच करेगा और जब भी आवश्यक (धीमी) उसे आकार देगा। इसके साथ AddRange, संग्रह की क्षमता निर्धारित की जाएगी और फिर आइटम (तेजी से) जोड़े जाएंगे। यह विस्तार विधि बेहद धीमी होगी, लेकिन काम करेगी।


3
इसे जोड़ने के लिए, AddRange के साथ एक बल्क नोटिफिकेशन के विपरीत, प्रत्येक जोड़ के लिए एक संग्रह परिवर्तन अधिसूचना भी होगी।
निक उडेल सेप

3

यहाँ थोड़ा और अधिक उन्नत / उत्पादन-तैयार संस्करण है:

    public static class CollectionExtensions
    {
        public static TCol AddRange<TCol, TItem>(this TCol destination, IEnumerable<TItem> source)
            where TCol : ICollection<TItem>
        {
            if(destination == null) throw new ArgumentNullException(nameof(destination));
            if(source == null) throw new ArgumentNullException(nameof(source));

            // don't cast to IList to prevent recursion
            if (destination is List<TItem> list)
            {
                list.AddRange(source);
                return destination;
            }

            foreach (var item in source)
            {
                destination.Add(item);
            }

            return destination;
        }
    }

rymdsmurf का उत्तर बहुत सरल लग सकता है, लेकिन यह विषम सूचियों के साथ काम करता है। क्या इस कोड को इस उपयोग मामले का समर्थन करना संभव है?
रिचारडसनट्रॉट

जैसे: एक सार वर्ग destinationकी एक सूची है Shape। एक विरासत वर्ग sourceकी एक सूची है Circle
रिचारडसनट्रॉट

1

सी 5 जेनेरिक संग्रह पुस्तकालय वर्गों सभी का समर्थन AddRangeविधि। C5 में बहुत अधिक मजबूत इंटरफ़ेस है जो वास्तव में इसके अंतर्निहित कार्यान्वयन की सभी विशेषताओं को उजागर करता है System.Collections.Generic ICollectionऔर IListइंटरफेस और इंटरफेस के साथ संगत है, जिसका अर्थ है कि C5अंतर्निहित कार्यान्वयन के रूप में संग्रह आसानी से प्रतिस्थापित किया जा सकता है।


0

आप अपनी IEnumerable रेंज को सूची में जोड़ सकते हैं, फिर सूची में ICollection = सेट करें।

        IEnumerable<T> source;

        List<item> list = new List<item>();
        list.AddRange(source);

        ICollection<item> destination = list;

3
जबकि यह कार्यात्मक रूप से काम करता है, यह संग्रह के गुणों को पढ़ने के लिए Microsoft दिशानिर्देशों को तोड़ता है ( msdn.microsoft.com/en-us/library/ms182327.aspx )
Nick Udell

0

या आप बस इस तरह एक ICollection विस्तार कर सकते हैं:

 public static ICollection<T> AddRange<T>(this ICollection<T> @this, IEnumerable<T> items)
    {
        foreach(var item in items)
        {
            @this.Add(item);
        }

        return @this;
    }

इसका उपयोग करना किसी सूची पर इसका उपयोग करने जैसा होगा:

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