นอกจากนี้หลังจากการแสดงความคิดเห็นที่มีประโยชน์มากของ 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
ๆ คุณระบุเฉพาะ N ชิ้นแรกเท่านั้น คุณจะต้องระบุองค์ประกอบ M แรกเท่านั้น
While(source.Any())
{
...
}
Any จะได้รับ Enumerator ทำ 1 MoveNext () และส่งคืนค่าที่ส่งคืนหลังจากการกำจัด Enumerator สิ่งนี้จะทำ N ครั้ง
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;
}
}
สิ่งนี้ไม่ได้ทำอะไรมากมายจนกระทั่งคุณเริ่มแจกแจงกับ Chunk ที่ดึงมา หากคุณดึงชิ้นส่วนหลายอัน แต่ตัดสินใจที่จะไม่แจกแจงชิ้นแรกชุด foreach จะไม่ถูกดำเนินการเนื่องจากโปรแกรมดีบั๊กของคุณจะแสดงให้คุณเห็น
หากคุณตัดสินใจที่จะใช้องค์ประกอบ 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()
หนึ่งครั้งสำหรับทุกองค์ประกอบใน Chunk มันไม่ได้เรียกCurrent
ดังนั้นต่อกลุ่มเราจะเห็นว่าทำต่อไปนี้:
- ใด ๆ (): GetEnumerator; 1 MoveNext (); กำจัด Enumerator;
ใช้ ():
- ไม่มีอะไรถ้าเนื้อหาของก้อนไม่แจกแจง
หากเนื้อหามีการแจกแจง: GetEnumerator () หนึ่ง MoveNext และหนึ่งในปัจจุบันต่อรายการที่ระบุจำหน่ายทิ้งตัวระบุ;
ข้าม (): สำหรับทุก ๆ ชิ้นที่มีการแจกแจง (ไม่ใช่เนื้อหาของก้อน): GetEnumerator (), MoveNext () เวลา chunkSize ไม่มีกระแส! กำจัดตัวแจงนับ
หากคุณดูว่าเกิดอะไรขึ้นกับตัวแจงนับคุณจะเห็นว่ามีการเรียกใช้ MoveNext () จำนวนมากและมีเพียงการโทรไปยังCurrent
รายการ TSource ที่คุณตัดสินใจเข้าถึง
หากคุณใช้ chunkSize ขนาด N จำนวนแล้วโทรไปที่ MoveNext ()
- ไม่มีเวลาใด ๆ ()
- ยังไม่มีเวลาสำหรับ Take ตราบใดที่คุณยังไม่แจกแจง Chunks
- ก้อนไม่มีครั้งขนาดสำหรับการข้าม ()
หากคุณตัดสินใจที่จะระบุเฉพาะองค์ประกอบ M แรกของชิ้นข้อมูลที่ดึงมาทั้งหมดคุณจะต้องเรียกใช้ MoveNext M คูณต่อ Chunk ที่ระบุ
ผลรวม
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 () ก่อนที่คุณจะเริ่มแบ่งรายการใน Chunks