अंत में mhand की बहुत उपयोगी टिप्पणी के बाद जोड़
मूल उत्तर
यद्यपि अधिकांश समाधान काम कर सकते हैं, मुझे लगता है कि वे बहुत कुशलता से नहीं हैं। मान लीजिए अगर आप केवल पहले कुछ चंक्स के पहले आइटम चाहते हैं। तब आप अपने अनुक्रम में सभी (zillion) वस्तुओं पर पुनरावृति नहीं करना चाहेंगे।
निम्नलिखित दो बार अत्यधिक गणना करेगा: एक बार टेक के लिए और एक बार स्किप के लिए। यह आपके द्वारा उपयोग किए जाने वाले किसी भी अधिक तत्वों की गणना नहीं करेगा:
public static IEnumerable<IEnumerable<TSource>> ChunkBy<TSource>
(this IEnumerable<TSource> source, int chunkSize)
{
while (source.Any()) // while there are elements left
{ // still something to chunk:
yield return source.Take(chunkSize); // return a chunk of chunkSize
source = source.Skip(chunkSize); // skip the returned chunk
}
}
इस क्रम को कितनी बार दोहराना होगा?
मान लीजिए कि आप अपने स्रोत को विखंडू में विभाजित करते हैं chunkSize
। आप केवल पहले एन विखंडू की गणना करते हैं। हर एन्यूमरेटेड चंक से आप केवल पहले एम एलिमेंट्स को एन्यूमरेट करेंगे।
While(source.Any())
{
...
}
किसी को भी एन्यूमरेटर मिलेगा, 1 मूवनेट () करें और एन्यूमरेटर को डिस्पोज करने के बाद लौटाया हुआ मान लौटाएं। यह एन बार किया जाएगा
yield return source.Take(chunkSize);
संदर्भ स्रोत के अनुसार यह कुछ ऐसा करेगा:
public static IEnumerable<TSource> Take<TSource>(this IEnumerable<TSource> source, int count)
{
return TakeIterator<TSource>(source, count);
}
static IEnumerable<TSource> TakeIterator<TSource>(IEnumerable<TSource> source, int count)
{
foreach (TSource element in source)
{
yield return element;
if (--count == 0) break;
}
}
यह तब तक बहुत कुछ नहीं करता है जब तक कि आप भ्रूण वाले चंक के ऊपर से गुजरना शुरू नहीं करते हैं। यदि आप कई चंक्स लाते हैं, लेकिन यह तय करें कि पहले चंक पर अधिक ध्यान न दें, तो फॉरेस्ट निष्पादित नहीं होता है, क्योंकि आपका डिबगर आपको दिखाएगा।
यदि आप पहले चंक के पहले M तत्वों को लेने का निर्णय लेते हैं तो पैदावार रिटर्न बिल्कुल M बार निष्पादित किया जाता है। इसका मतलब है की:
- गणक प्राप्त करें
- MoveNext () और वर्तमान M समय को कॉल करें।
- एन्यूमरेटर को डिस्पोज करें
पहले चंक की उपज वापस आने के बाद, हम इस पहले चंक को छोड़ देते हैं:
source = source.Skip(chunkSize);
एक बार फिर: हम खोजने के लिए संदर्भ स्रोत पर एक नज़र डालेंगेskipiterator
static IEnumerable<TSource> SkipIterator<TSource>(IEnumerable<TSource> source, int count)
{
using (IEnumerator<TSource> e = source.GetEnumerator())
{
while (count > 0 && e.MoveNext()) count--;
if (count <= 0)
{
while (e.MoveNext()) yield return e.Current;
}
}
}
जैसा कि आप देखते हैं, चंक में हर तत्व के लिए एक बार SkipIterator
कॉल करता है MoveNext()
। यह फोन नहीं करता है Current
।
तो प्रति Chunk हम देखते हैं कि निम्नलिखित किया जाता है:
- कोई (): GetEnumerator; 1 MoveNext (); डिस्पोजल एन्यूमरेटर;
लेना():
- कुछ भी नहीं है अगर chunk की सामग्री enumerated नहीं है।
यदि सामग्री enumerated है: GetEnumerator (), एक MoveNext और एक वर्तमान प्रति enumerated आइटम, Disumerumer;
छोड़ें (): हर उस टुकड़ी के लिए जो एन्यूमरेटेड है (कंटेंट नहीं): GetEnumerator (), MoveNext () chunkSize times, no current! गणना करनेवाला
यदि आप देखते हैं कि एन्यूमरेटर के साथ क्या होता है, तो आप देखेंगे कि MoveNext () के लिए बहुत सारे कॉल हैं, और केवल Current
TSource आइटम के लिए कॉल करते हैं जिन्हें आप वास्तव में एक्सेस करने का निर्णय लेते हैं।
यदि आप आकार का एन चंक्स लेते हैं, तो MoveNext () को कॉल करें
- किसी भी समय ()
- टेक के लिए अभी तक कोई समय नहीं है, जब तक आप चंक्स की गणना नहीं करते हैं
- एन बार स्किप करने के लिए ()
यदि आप हर आने वाले चंक के केवल पहले M तत्वों की गणना करने का निर्णय लेते हैं, तो आपको प्रति Enumerated Chunk को MoveNext M बार कॉल करना होगा।
समूचा
MoveNext calls: N + N*M + N*chunkSize
Current calls: N*M; (only the items you really access)
इसलिए यदि आप सभी विखंडू के सभी तत्वों की गणना करने का निर्णय लेते हैं:
MoveNext: numberOfChunks + all elements + all elements = about twice the sequence
Current: every item is accessed exactly once
MoveNext बहुत काम है या नहीं, स्रोत अनुक्रम के प्रकार पर निर्भर करता है। सूचियों और सरणियों के लिए यह एक साधारण सूचकांक वृद्धि है, शायद एक सीमा से बाहर की जाँच।
लेकिन अगर आपका IEnumerable डेटाबेस क्वेरी का परिणाम है, तो सुनिश्चित करें कि डेटा वास्तव में आपके कंप्यूटर पर भौतिक है, अन्यथा डेटा को कई बार लाया जाएगा। DbContext और Dapper एक्सेस होने से पहले डेटा को स्थानीय प्रक्रिया में ठीक से स्थानांतरित कर देगा। यदि आप एक ही क्रम को कई बार दोहराते हैं तो यह कई बार नहीं आता है। Dapper एक ऑब्जेक्ट देता है जो एक सूची है, DbContext याद रखता है कि डेटा पहले से ही प्राप्त है।
यह आपके रिपॉजिटरी पर निर्भर करता है कि क्या चांस में आइटम विभाजित करना शुरू करने से पहले आपको AsEnumerable () या ToLists () को कॉल करना बुद्धिमान है