Linq đúng nơi mệnh đề


133

Tôi viết một số lượng khá lớn linq trong cuộc sống hàng ngày của tôi, nhưng chủ yếu là những tuyên bố đơn giản. Tôi đã nhận thấy rằng khi sử dụng mệnh đề ở đâu, có nhiều cách để viết chúng và mỗi cách đều có kết quả giống như tôi có thể nói. Ví dụ;

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

Xuất hiện tương đương với điều này ít nhất là về kết quả có liên quan:

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

Vì vậy, có thực sự có một sự khác biệt ngoài cú pháp? Nếu vậy, phong cách ưa thích là gì và tại sao?


203
Bạn có một Fattài sản boolean ? Đó là ý nghĩa đơn giản.
Bala R

104
@Bala R: Này, nếu con chó của bạn béo, con chó của bạn béo.
AR

Câu trả lời:


76

Cái thứ hai sẽ hiệu quả hơn vì nó chỉ có một vị từ để đánh giá đối với từng mục trong bộ sưu tập, như trong phần đầu tiên, nó áp dụng vị từ đầu tiên cho tất cả các mục trước và kết quả (được thu hẹp tại điểm này) được sử dụng cho vị ngữ thứ hai và như vậy. Các kết quả được thu hẹp trong mỗi lần vượt qua nhưng nó vẫn liên quan đến nhiều lần vượt qua.

Ngoài ra, chuỗi (phương thức đầu tiên) sẽ chỉ hoạt động nếu bạn đang ANDing các vị từ của bạn. Một cái gì đó như thế này x.Age == 10 || x.Fat == truesẽ không hoạt động với phương pháp đầu tiên của bạn.


1
Điều kiện ORing chuỗi có thể phần nào có thể sử dụng tiện ích mở rộng này: albahari.com/nutshell/predicatebuilder.aspx
jahu

142

EDIT: LINQ to Object không hoạt động như tôi mong đợi. Bạn có thể quan tâm đến bài viết trên blog mà tôi vừa viết về điều này ...


Chúng khác nhau về những gì sẽ được gọi - đầu tiên là tương đương với:

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

cái sau này tương đương với:

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

Bây giờ sự khác biệt thực sự tạo ra phụ thuộc vào việc thực hiện Wheređược gọi. Nếu đó là nhà cung cấp dựa trên SQL, tôi hy vọng cả hai sẽ tạo cùng một SQL. Nếu nó nằm trong LINQ to Object, thì cái thứ hai sẽ có ít mức độ gián tiếp hơn (sẽ chỉ có hai vòng lặp liên quan thay vì bốn). Liệu các mức độ gián tiếp có đáng kể hay không về mặt tốc độ hay không là một vấn đề khác.

Thông thường tôi sẽ sử dụng một số wheremệnh đề nếu chúng cảm thấy như chúng đại diện cho các điều kiện khác nhau đáng kể (ví dụ: một điều phải làm với một phần của đối tượng và một wheremệnh đề hoàn toàn riêng biệt) và một mệnh đề khi các điều kiện khác nhau có liên quan chặt chẽ với nhau (ví dụ: một giá trị cụ thể lớn hơn mức tối thiểu và nhỏ hơn mức tối đa). Về cơ bản, nó đáng để xem xét khả năng đọc trước bất kỳ sự khác biệt hiệu suất nhỏ.


1
@JonSkeet Có thể tôi sai, nhưng sau khi xem xét nhanh về triển khai Linq Where, tôi không chắc về điều đó. Lồng nhau ở đâu được kết hợp bởi một phương thức tĩnh 'CombinePredicates'. Bộ sưu tập chỉ được lặp lại một lần bởi một trình vòng lặp duy nhất với biến vị ngữ kết hợp. Tất nhiên, có một tác động hiệu suất của việc kết hợp func, nhưng nó rất hạn chế. Bạn ổn chứ ?
Cybermaxs

@Cybermaxs: Không chắc chắn về những gì , chính xác? Tôi không bao giờ đề nghị rằng bộ sưu tập sẽ được lặp đi lặp lại nhiều lần.
Jon Skeet

@JonSkeet có tất nhiên nhưng cuối cùng tất cả các vị từ được kết hợp và chỉ có một trình vòng lặp được tham gia. Nhìn vàoEnumerable.WhereSelectEnumerableIterator.
Cybermaxs

Trang bạn liên kết đến bây giờ không hoạt động. Bạn có thể vui lòng cập nhật liên kết nếu bài viết vẫn còn ở một nơi khác? Cảm ơn.
Asad Saeeduddin

2
@Asad: Đã cập nhật. (Blog của tôi đã được chuyển đi.)
Jon Skeet

13

Việc đầu tiên sẽ được thực hiện:

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

Trái ngược với đơn giản hơn nhiều (và nhanh hơn nhiều có lẽ nhanh hơn):

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

6
"Nhanh hơn nhiều"? Chúng tôi thậm chí không biết triển khai LINQ nào có liên quan, vì vậy thật khó để gắn bất kỳ hàm ý hiệu suất nào vào nó.
Jon Skeet

Trong trường hợp chung, cái sau chỉ cần 1 vòng lặp. Một nhà cung cấp có thể chọn làm phẳng ví dụ đầu tiên, nhưng không bắt buộc.
user7116

2
Trên thực tế ... nhưng bạn đang xác nhận sau này xa nhanh hơn. Không rõ ràng rằng nó sẽ nhanh hơn đáng kể - sau tất cả, tầm quan trọng của sự khác biệt hiệu suất sẽ phụ thuộc vào cách sử dụng nó.
Jon Skeet

1
@Jon: không bất đồng. Như bạn lưu ý, thực tế có thể là nhà cung cấp LINQ thực hiện và thực hiện các chuyển đổi tối ưu hóa hữu ích cho biểu thức. Nhưng với cái thứ hai chỉ cần một vòng lặp và lợi ích từ việc đoản mạch boolean, thật khó để hiểu tại sao nó không nên được gắn nhãn là "nhanh hơn nhiều" theo thuật ngữ chung. Nếu OP chỉ có 5 yếu tố thì điểm của tôi là moot.
dùng7116

11

khi tôi chạy

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

đối với bảng Khách hàng của tôi, nó xuất ra cùng một truy vấn 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)

Vì vậy, trong bản dịch sang sql không có sự khác biệt và bạn đã thấy trong các câu trả lời khác về cách chúng sẽ được chuyển đổi thành biểu thức lambda


ok, sau đó bạn muốn nói rằng nó sẽ không có bất kỳ hiệu ứng hiệu suất nếu tôi sử dụng bất kỳ trong số này?
Bimal Das

Mệnh đề WHERE bị xiềng xích trong thực tế. Vì vậy, nó không quan trọng bằng cách bạn viết nó. Không có sự khác biệt hiệu suất.
hastrb

3

Nhìn dưới mui xe, hai câu lệnh sẽ được chuyển thành các biểu diễn truy vấn khác nhau. Tùy thuộc vào QueryProvidercác Collection, điều này có thể được tối ưu hóa đi hay không.

Khi đây là một cuộc gọi linq-to-object, nhiều mệnh đề sẽ dẫn đến một chuỗi các IEnumerables đọc lẫn nhau. Sử dụng mẫu đơn mệnh đề sẽ giúp thực hiện ở đây.

Khi nhà cung cấp cơ bản dịch nó thành một câu lệnh SQL, rất có thể cả hai biến thể sẽ tạo ra cùng một câu lệnh.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.