คำสั่ง LINQ ต่อไปนี้ทำงานอย่างไร


160

คำสั่งLINQต่อไปนี้ทำงานอย่างไร

นี่คือรหัสของฉัน:

var list = new List<int>{1,2,4,5,6};
var even = list.Where(m => m%2 == 0);
list.Add(8);
foreach (var i in even)
{
    Console.WriteLine(i);
}

เอาท์พุท: 2, 4, 6, 8

ทำไม2, 4, 6ล่ะ


102
ผลลัพธ์ของนิพจน์แบบสอบถามคือแบบสอบถามไม่ใช่การดำเนินการของแบบสอบถาม
Eric Lippert

6
สำหรับข้อมูลน้อยลงให้ดูคำตอบที่ยอมรับสำหรับคำถามนี้
Daniel

9
แน่นอนคุณสามารถคิดถึงชื่อที่สรุปคำถามได้อย่างแท้จริง
Matt Ball

2
ฉันเดาเกี่ยวกับ downvotes (6 ตอนนี้ไม่ใช่ของฉัน) คือพวกเขาพิจารณาชื่อคำถามทั่วไปเกินไปที่จะเป็นคำถามที่ดี แต่เมื่อเห็นจำนวนผู้โหวตและกลายเป็นคำถามยอดนิยมประจำสัปดาห์ในจดหมายข่าวฉันไม่คิดว่าคุณต้องกังวลเกี่ยวกับเรื่องนี้มากเกินไป
Abel

คำตอบ:


235

เอาท์พุทเป็น2,4,6,8เพราะการดำเนินการรอการตัดบัญชี

จริงแล้วแบบสอบถามจะถูกดำเนินการเมื่อมีการทำซ้ำตัวแปรของแบบสอบถามไม่ใช่เมื่อสร้างตัวแปรคิวรี สิ่งนี้เรียกว่าการดำเนินการรอการตัดบัญชี

- Suprotim Agarwal "การดำเนินการสืบค้นข้อมูลที่ล่าช้าออกไปทันทีใน LINQ"

มีการดำเนินการอื่นที่เรียกว่าการดำเนินการค้นหาทันทีซึ่งมีประโยชน์สำหรับการแคชผลลัพธ์แบบสอบถาม จาก Suprotim Agarwal อีกครั้ง:

เมื่อต้องการบังคับให้เรียกใช้งานการสืบค้นทันทีที่ไม่ได้สร้างค่า Singleton คุณสามารถเรียกใช้ToList(), ToDictionary(), ToArray(), Count(), Average()หรือMax() วิธีการในแบบสอบถามหรือตัวแปรแบบสอบถาม สิ่งเหล่านี้เรียกว่าโอเปอเรเตอร์การแปลงซึ่งอนุญาตให้คุณทำสำเนา / สแนปชอตของผลลัพธ์และการเข้าถึงมีหลายครั้งที่คุณต้องการโดยไม่จำเป็นต้องเรียกใช้แบบสอบถามอีกครั้ง

หากคุณต้องการให้เอาต์พุต2,4,6ใช้.ToList():

var list = new List<int>{1,2,4,5,6};
var even = list.Where(m => m%2 == 0).ToList();
list.Add(8);
foreach (var i in even)
 {
    Console.WriteLine(i);
 }

8
Count (), Max (), Avg (), Sum () และวิธีการอื่น ๆ ที่อาจจำเป็นต้องพิจารณารายการทั้งหมดรวมถึงการประเมินผลของแบบสอบถาม
Kenned

1
ฉันมักจะคิดเกี่ยวกับการพูด 'filteredList' เป็นตัวแปรแทนที่จะเป็น 'filterList ()' เป็นวิธีหนึ่งความคิดคือคุณวนซ้ำทุกครั้งที่คุณต้องการกรองรายการแทนที่จะเรียกวิธีการ อาจเป็นวิธีการที่น่าสนใจถ้าผิดปกติและอาจไม่สมบูรณ์แบบ
Katana314

4
@Sebastian - ต่อไป @ คิดเห็น Kenned ของ.First(), .FirstOrDefault(), .Single()และ.SingleOrDefault()ยังก่อให้เกิดการประเมินผลของแบบสอบถาม
Scotty.NET

4
น่าประหลาดใจที่คุณได้รับคำตอบน้อยกว่า 30 วินาที: D
MC

2
@MC ฉันไม่รู้ว่าทำไมคุณถึงถามคำถามนี้ ไม่ได้รับคำตอบทั้งหมดในเวลาเดียวกัน มีการแก้ไขหลายครั้ง
Atish Dipongkor - MVP

11

สิ่งนี้เกิดขึ้นเนื่องจากการประมวลผลที่เลื่อนออกไปซึ่งหมายความว่าการคำนวณนิพจน์จะไม่ถูกดำเนินการจนกว่าจะจำเป็นในบางที่ ทำให้ประสิทธิภาพดีขึ้นหากข้อมูลมีขนาดใหญ่เกินไป


3
คุณอาจแตกต่างกันเล็กน้อยเนื่องจากอาจหมายถึงการดำเนินการแจงนับราคาแพงของคุณหลายครั้ง ในกรณีเช่นนี้คุณอาจประสบกับการสูญเสียประสิทธิภาพ
หน้าตาบูดบึ้งของ Despair

0

เหตุผลนี้คือการประมวลผลนิพจน์แลมบ์ดาที่คุณรอตัดบัญชี แบบสอบถามได้รับการดำเนินการเมื่อคุณเริ่มทำซ้ำใน foreach loop


11
ในทางเทคนิคมันเป็นการดำเนินการรอการตัดบัญชีของiteratorไม่แลมบ์ดา
D Stanley

0

เมื่อคุณใช้ IEnumerable <> ที่ได้รับจาก LINQ จะมีการสร้างคลาส Enumerator เท่านั้นและการวนซ้ำจะเริ่มเมื่อคุณใช้ในการเดิน


-1

คุณได้รับผลลัพธ์นี้เนื่องจากการประมวลผลที่เลื่อนออกไปซึ่งหมายความว่าผลลัพธ์จะไม่ถูกประเมินจนกว่าจะมีการเข้าถึงครั้งแรก

เพื่อให้ชัดเจนยิ่งขึ้นเพียงเพิ่ม 10 ลงในรายการที่ส่วนท้ายของตัวอย่างแล้วพิมพ์อีกครั้งคุณจะไม่ได้รับ 10 ผลลัพธ์

     var list = new List<int>{1,2,4,5,6};
    var even = list.Where(m => m%2 == 0).Tolist();
    list.Add(8);
    foreach (var i in even)
    {
        Console.WriteLine(i);
    }
//new*
    list.Add(10);
    foreach (var i in even)
    {
        Console.WriteLine(i);
    }

คุณลองสิ่งนั้นจริงหรือ ฉันได้รับ10ผลลัพธ์
Mark Hurd

good catch @MarkHurd ใช่ไม่ได้เพิ่ม. ToList () แก้ไขโพสต์ตอนนี้ควรให้ผลลัพธ์ที่คาดหวัง ความคาดหวังของฉันคือการแสดงออกจะได้รับการประเมินเฉพาะเมื่อคุณใช้ var เป็นครั้งแรก แต่ดูเหมือนว่าจะได้รับการประเมินทุกครั้ง
sandeep

ตอนนี้มันจะไม่มี8ในเอาท์พุทอย่างใดอย่างหนึ่ง
Mark Hurd
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.