Linq ที่เหมาะสมที่ข้อ


134

ฉันเขียน linq จำนวนพอสมควรในชีวิตประจำวันของฉัน แต่ส่วนใหญ่เป็นข้อความง่ายๆ ฉันสังเกตเห็นว่าเมื่อใช้ where clauses มีหลายวิธีในการเขียนและแต่ละส่วนมีผลลัพธ์เหมือนกันเท่าที่ฉันสามารถบอกได้ ตัวอย่างเช่น;

from x in Collection
  where x.Age == 10
  where x.Name == "Fido"
  where x.Fat == true
  select x;

ดูเหมือนว่าอย่างน้อยที่สุดเท่าที่ผลลัพธ์จะเกี่ยวข้อง:

from x in Collection
  where x.Age == 10 &&
        x.Name == "Fido" &&
        x.Fat == true
  select x;

ดังนั้นมีความแตกต่างนอกเหนือจากไวยากรณ์หรือไม่? ถ้าเป็นเช่นนั้นสไตล์ที่ต้องการคืออะไรและเพราะเหตุใด

คำตอบ:


76

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

นอกจากนี้การผูกมัด (วิธีแรก) จะใช้ได้เฉพาะเมื่อคุณเป็น ANDing เพรดิเคตของคุณ สิ่งนี้x.Age == 10 || x.Fat == trueจะใช้ไม่ได้กับวิธีแรกของคุณ


1
เงื่อนไข Chain ORing ค่อนข้างเป็นไปได้โดยใช้ส่วนขยายนี้: albahari.com/nutshell/predicatebuilder.aspx
jahu

143

แก้ไข: LINQ to Objects ไม่ทำงานตามที่ฉันคาดหวังไว้ คุณอาจสนใจในบล็อกโพสต์ที่ฉันเพิ่งเขียนเกี่ยวกับเรื่องนี้ ...


พวกเขามีความแตกต่างกันในแง่ของสิ่งที่จะเรียก - ครั้งแรกเทียบเท่ากับ:

Collection.Where(x => x.Age == 10)
          .Where(x => x.Name == "Fido")
          .Where(x => x.Fat == true)

ในขณะที่หลังเทียบเท่ากับ:

Collection.Where(x => x.Age == 10 && 
                      x.Name == "Fido" &&
                      x.Fat == true)

ตอนนี้ความแตกต่างที่เกิดขึ้นจริงขึ้นอยู่กับการใช้งานWhereที่เรียกว่า หากเป็นผู้ให้บริการที่ใช้ SQL ฉันคาดหวังว่าทั้งสองจะสร้าง SQL เดียวกัน หากอยู่ใน LINQ ถึง Objects ระดับที่สองจะมีระดับการบ่งชี้น้อยกว่า (จะมีตัวทำซ้ำเพียงสองตัวที่เกี่ยวข้องแทนที่จะเป็นสี่ตัว) ระดับของทิศทางเหล่านั้นมีความสำคัญในแง่ของความเร็วหรือไม่นั้นเป็นเรื่องที่แตกต่างกัน

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


1
@JonSkeet บางทีฉันอาจจะผิด แต่หลังจากการตรวจสอบอย่างรวดเร็วของ Linq Where การใช้งานฉันไม่แน่ใจในเรื่องนั้น ซ้อนกันโดยวิธีการแบบคงที่ 'CombinePredicates' คอลเลกชันจะวนซ้ำเพียงครั้งเดียวโดยตัววนซ้ำตัวเดียวที่มีเพรดิเคตรวม แน่นอนว่ามีผลกระทบด้านประสิทธิภาพของการรวม func แต่มีข้อ จำกัด มาก คุณโอเคไหม ?
Cybermaxs

@ Cybermaxs: ไม่แน่ใจว่าอะไร ? ฉันไม่เคยแนะนำว่าคอลเลกชันจะถูกทำซ้ำมากกว่าหนึ่งครั้ง
Jon Skeet

@JonSkeet ใช่แน่นอน แต่ในตอนท้ายเพรดิเคตทั้งหมดจะถูกรวมเข้าด้วยกันและตัววนซ้ำเพียงตัวเดียวจะถูกเรียก ดูที่ Enumerable.WhereSelectEnumerableIterator
Cybermaxs

หน้าที่คุณเชื่อมโยงไม่สามารถใช้งานได้ในขณะนี้ คุณช่วยอัปเดตลิงค์ได้ไหมถ้าบทความยังอยู่ที่อื่น ขอบคุณ.
Asad Saeeduddin

2
@ Asad: อัพเดท (บล็อกของฉันย้ายไปแล้ว)
Jon Skeet

13

สิ่งแรกจะถูกนำไปใช้:

Collection.Where(x => x.Age == 10)
          .Where(x => x.Name == "Fido") // applied to the result of the previous
          .Where(x => x.Fat == true)    // applied to the result of the previous

ตรงข้ามกับวิธีที่ง่ายกว่ามาก (และเร็วกว่ามากซึ่งน่าจะเร็วกว่า):

// all in one fell swoop
Collection.Where(x => x.Age == 10 && x.Name == "Fido" && x.Fat == true)

6
“ เร็วกว่า”? เราไม่รู้ด้วยซ้ำว่าการใช้งาน LINQ ใดที่เกี่ยวข้องดังนั้นจึงเป็นการยากที่จะแนบผลกระทบด้านประสิทธิภาพใด ๆ
Jon Skeet

ในกรณีทั่วไปต้องใช้ 1 ลูปเท่านั้น ผู้ให้บริการสามารถเลือกที่จะแบนตัวอย่างแรกได้ แต่ไม่จำเป็นต้องใช้
user7116

2
อันที่จริง ... แต่คุณอ้างว่าหลังมีการไกลเร็ว ไม่ชัดเจนเลยว่ามันจะเร็วขึ้นอย่างมีนัยสำคัญ - ท้ายที่สุดแล้วความสำคัญของความแตกต่างของประสิทธิภาพจะขึ้นอยู่กับวิธีการใช้งาน
Jon Skeet

1
@ จอน: ไม่เห็นด้วย ดังที่คุณทราบความเป็นจริงอาจเป็นเพราะผู้ให้บริการ LINQ ไปและทำการแปลงการเพิ่มประสิทธิภาพที่เป็นประโยชน์กับนิพจน์ แต่เนื่องจากอันที่สองต้องการเพียงลูปเดียวและได้รับประโยชน์จากการลัดวงจรแบบบูลีนจึงยากที่จะเห็นว่าเหตุใดจึงไม่ควรระบุว่า "เร็วกว่ามาก" ในแง่ทั่วไป หาก OP มีเพียง 5 องค์ประกอบจุดของฉันคือ moot
user7116

11

เมื่อฉันวิ่ง

from c in Customers
where c.CustomerID == 1
where c.CustomerID == 2
where c.CustomerID == 3
select c

และ

from c in Customers
where c.CustomerID == 1 &&
c.CustomerID == 2 &&
c.CustomerID == 3
select c customer table in linqpad

เมื่อเทียบกับตารางลูกค้าของฉันมันจะส่งออกแบบสอบถาม sql เดียวกัน

-- Region Parameters
DECLARE @p0 Int = 1
DECLARE @p1 Int = 2
DECLARE @p2 Int = 3
-- EndRegion
SELECT [t0].[CustomerID], [t0].[CustomerName]
FROM [Customers] AS [t0]
WHERE ([t0].[CustomerID] = @p0) AND ([t0].[CustomerID] = @p1) AND ([t0].[CustomerID] = @p2)

ดังนั้นในการแปล sql จึงไม่มีความแตกต่างและคุณได้เห็นแล้วในคำตอบอื่น ๆ ว่าจะแปลงเป็นนิพจน์แลมบ์ดาได้อย่างไร


โอเคคุณอยากจะบอกว่ามันจะไม่มีผลต่อประสิทธิภาพใด ๆ ถ้าฉันใช้สิ่งเหล่านี้?
Bimal Das

โดยที่ประโยคถูกผูกมัดในความเป็นจริง ดังนั้นมันไม่สำคัญว่าคุณจะเขียนมันอย่างไร ไม่มีความแตกต่างของประสิทธิภาพ
hastrb

3

เมื่อมองภายใต้ฝากระโปรงข้อความทั้งสองจะถูกเปลี่ยนเป็นการแทนคำค้นหาที่แตกต่างกัน ทั้งนี้ขึ้นอยู่กับQueryProviderการCollectionนี้อาจถูกปรับออกไปหรือไม่

เมื่อนี่คือการเรียก linq-to-object หลาย ๆ ตัวโดยที่ clauses จะนำไปสู่ ​​chain ของ IEnumerables ที่อ่านจากกัน การใช้แบบฟอร์ม single-clause จะช่วยเพิ่มประสิทธิภาพที่นี่

เมื่อผู้ให้บริการพื้นฐานแปลเป็นคำสั่ง SQL โอกาสที่ทั้งสองตัวแปรจะสร้างคำสั่งเดียวกัน

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