รหัส Linq เพื่อเลือกหนึ่งรายการ


105

ฉันพบว่าตัวเองเขียนโค้ดจำนวนมากเช่นนี้เพื่อเลือกรายการที่ตรงกัน

var item = (from x in Items where x.Id == 123 select x).First();

มีวิธีทำที่สะอาดกว่านี้ไหมหรือจะรวบรัดเท่าที่ฉันจะได้รับ?

แก้ไข: ควรจะพูดว่า "วิธีที่สะอาดกว่าโดยใช้ไวยากรณ์ linq" ฉันรู้ไวยากรณ์แลมบ์ดาแล้วและมันเริ่มดูเหมือนว่านี่เป็นวิธีเดียวเท่านั้น ฉันได้รับข้อมูลที่เป็นประโยชน์ดังนั้นขอบคุณทุกคนที่ตอบกลับ


5
โดยส่วนตัวแล้วฉันหลีกเลี่ยงSingle()และSingleOrDefault()ถ้าฉันรู้ว่าข้อมูลนั้นไม่ซ้ำกันอยู่แล้ว (เช่นจากฐานข้อมูลที่มีข้อ จำกัด เป็นต้น) เนื่องจากSingle()บังคับให้สแกนส่วนที่เหลือของรายการเพื่อค้นหารายการที่ซ้ำกันได้ แต่นั่นคือฉัน หากคุณจำเป็นต้องบังคับใช้ความเป็นเอกลักษณ์ ณ จุดนี้ให้ใช้Single()ครอบครัวถ้าไม่ใช้First()ครอบครัว
James Michael Hare

คำตอบ:


177

ขึ้นอยู่กับว่าคุณชอบไวยากรณ์แบบสอบถาม linq มากแค่ไหนคุณสามารถใช้วิธีการขยายได้โดยตรงเช่น:

var item = Items.First(i => i.Id == 123);

และถ้าคุณไม่ต้องการให้เกิดข้อผิดพลาดหากรายการว่างเปล่าให้ใช้FirstOrDefaultซึ่งส่งคืนค่าเริ่มต้นสำหรับประเภทองค์ประกอบ ( nullสำหรับประเภทการอ้างอิง):

var item = Items.FirstOrDefault(i => i.Id == 123);

if (item != null)
{
    // found it
}

Single()และSingleOrDefault()ยังสามารถใช้งานได้ แต่ถ้าคุณกำลังอ่านจากฐานข้อมูลหรือสิ่งที่รับประกันความเป็นเอกลักษณ์อยู่แล้วฉันจะไม่รำคาญเพราะต้องสแกนรายการเพื่อดูว่ามีรายการที่ซ้ำกันหรือไม่ First()และFirstOrDefault()หยุดในนัดแรกดังนั้นพวกเขาจึงมีประสิทธิภาพมากขึ้น

ของครอบครัวFirst()และSingle()ครอบครัวนี่คือที่ที่พวกเขาโยน:

  • First() - โยนถ้าว่าง / ไม่พบไม่โยนถ้าซ้ำกัน
  • FirstOrDefault() - ส่งคืนค่าเริ่มต้นหากว่างเปล่า / ไม่พบไม่โยนหากซ้ำกัน
  • Single() - พ่นหากว่างเปล่า / ไม่พบพ่นหากมีรายการซ้ำ
  • SingleOrDefault() - ส่งคืนค่าเริ่มต้นหากว่างเปล่า / ไม่พบพ่นหากมีรายการซ้ำ

1
ฉันคิดว่าคุณหายไปสองสัญญาณเท่ากับตรงนั้น ควรจะเป็นi.Id == 123
davehale23

18

FirstOrDefaultหรือSingleOrDefaultอาจมีประโยชน์ขึ้นอยู่กับสถานการณ์ของคุณและคุณต้องการจัดการว่ามีการจับคู่เป็นศูนย์หรือมากกว่าหนึ่งรายการ:

FirstOrDefault: ส่งคืนองค์ประกอบแรกของลำดับหรือค่าเริ่มต้นหากไม่พบองค์ประกอบ

SingleOrDefault: ส่งคืนองค์ประกอบเดียวของลำดับหรือค่าเริ่มต้นหากลำดับว่างเปล่า วิธีนี้จะแสดงข้อยกเว้นหากมีองค์ประกอบมากกว่าหนึ่งรายการในลำดับ

ฉันไม่รู้ว่ามันทำงานอย่างไรในแบบสอบถาม linq 'from' แต่ในไวยากรณ์แลมบ์ดาดูเหมือนว่า:

var item1 = Items.FirstOrDefault(x => x.Id == 123);
var item2 = Items.SingleOrDefault(x => x.Id == 123);

ข้อใดมีประสิทธิภาพมากที่สุดในแง่ของเวลา DB หากฉันมี varchar ฉันต้องการการจับคู่แบบตรงทั้งหมด แต่เป็นไปได้ไม่มีเลย?
Piotr Kula

2
คำตอบที่ยอมรับจะกล่าวถึงสิ่งนี้อย่างเต็มรูปแบบ แต่โดยพื้นฐานแล้ว FirstOrDefault จะหยุดทันทีที่พบรายการที่ตรงกัน แต่ SingleOrDefault ต้องตรวจสอบรายการทั้งหมดเพื่อให้แน่ใจว่ามีรายการที่ตรงกันทั้งหมด
stuartd

12

วิธีการเหล่านี้เป็นวิธีที่ต้องการ:

var item = Items.SingleOrDefault(x => x.Id == 123);

หรือ

var item = Items.Single(x => x.Id == 123);

ขอบคุณ - ในสถานการณ์นี้ไม่มีสัญกรณ์ linq และฉันต้องใช้ lambdas?
Mikey Hogarth

ค่อนข้างดี ฉันไม่ได้ใช้ linq มากนักดังนั้นฉันไม่แน่ใจว่าฉันรู้วิธีเหล่านี้ด้วยซ้ำ
wageoghe

1
วิธีเดียวตรวจสอบว่าค่าที่ส่งคืนไม่ซ้ำกัน ดังนั้นหากคอลเล็กชันของคุณมีขนาดใหญ่อาจใช้เวลามาก วิธีแรกเพียงแค่ส่งคืนองค์ประกอบแรกที่ตรงกับเพรดิเคต
meziantou

คำตอบของ @wageoghe James ไม่ได้ใช้ linq - เมธอด Single และ SingleOrDefault เป็นส่วนหนึ่งของการใช้งาน IEnumerable
Mikey Hogarth

12

เพียงเพื่อทำให้ชีวิตของใครบางคนง่ายขึ้นแบบสอบถาม linq พร้อมนิพจน์แลมบ์ดา

(from x in Items where x.Id == 123 select x).FirstOrDefault();

ส่งผลให้แบบสอบถาม SQL ที่มี select top (1)อยู่ในนั้น


9

ที่สามารถย่อลงได้ดีกว่านี้

var item = Items.First(x => x.Id == 123);

การค้นหาของคุณในปัจจุบันคือการเก็บรวบรวมผลการค้นหาทั้งหมด (และอาจจะมีมากกว่าหนึ่ง) ภายในนับแล้วการเป็นคนแรกจากนั้นชุดทำงานเกินกว่าที่จำเป็น

Single / SingleOrDefault นั้นคุ้มค่า แต่ถ้าคุณต้องการทำซ้ำในคอลเล็กชันทั้งหมดและตรวจสอบว่าการจับคู่ไม่ซ้ำกันนอกเหนือจากการเลือกการจับคู่นั้น First / FirstOrDefault จะทำการจับคู่ครั้งแรกและจากไปโดยไม่คำนึงถึงจำนวนที่ซ้ำกัน


4

คุณสามารถใช้ไวยากรณ์วิธีการขยาย:

var item = Items.Select(x => x.Id == 123).FirstOrDefault();

นอกเหนือจากนั้นฉันไม่แน่ใจว่าคุณจะกระชับได้มากเพียงใดโดยไม่ต้องเขียนวิธีการขยาย "First" และ "FirstOrDefault" เฉพาะของคุณเอง


ฉันไม่คิดว่านี่เป็นพฤติกรรมที่ตั้งใจไว้ คำสั่ง Select นี้จะส่งคืน (จริง ๆ แล้วไม่ได้ แต่จะเลือกสำหรับการส่งคืน) ชุดบูลหนึ่งชุดสำหรับแต่ละองค์ประกอบของรายการสิ่งแรกที่ส่งคืนเป็นรายการซึ่งจะกลายเป็นเพียงบูล ใช้วิธีแก้ปัญหาของ Chris Hannon
Leonardo Daga

บางทีคุณอาจหมายถึงWhereตรงข้ามSelectซึ่งได้ระบุไว้แล้ว แต่คำตอบนี้ไม่ถูกต้อง เลือกใน c # เปลี่ยนผลลัพธ์เป็น IEnumerable <bool> ดังนั้นคุณจะได้รับboolสำหรับรายการแรกx.Id == 123
bradlis7

2

ฉันจะบอกคุณว่าอะไรที่เหมาะกับฉัน:

int id = int.Parse(insertItem.OwnerTableView.DataKeyValues[insertItem.ItemIndex]["id_usuario"].ToString());

var query = user.First(x => x.id_usuario == id);
tbUsername.Text = query.username;
tbEmail.Text = query.email;
tbPassword.Text = query.password;

id ของฉันคือแถวที่ฉันต้องการสอบถามในกรณีนี้ฉันได้มาจาก radGrid จากนั้นฉันก็ใช้มันเพื่อสอบถาม แต่แบบสอบถามนี้ส่งคืนแถวจากนั้นคุณสามารถกำหนดค่าที่คุณได้รับจากการสืบค้นไปยังกล่องข้อความหรืออะไรก็ได้ ฉันต้องกำหนดสิ่งเหล่านั้นให้กับกล่องข้อความ

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