Linq yang tepat di mana klausa


133

Saya menulis cukup banyak linq dalam kehidupan saya sehari-hari, tetapi kebanyakan pernyataan sederhana. Saya perhatikan bahwa ketika menggunakan klausa mana, ada banyak cara untuk menulisnya dan masing-masing memiliki hasil yang sama sejauh yang saya tahu. Sebagai contoh;

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

Tampaknya setara dengan ini setidaknya sejauh menyangkut hasil:

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

Jadi, apakah memang ada perbedaan selain sintaks? Jika demikian, apa gaya yang disukai dan mengapa?


203
Anda memiliki Fatproperti boolean ? Itu sangat berarti.
Bala R

104
@Bala R: Hei, jika anjing Anda gemuk, anjing Anda gemuk.
AR

Jawaban:


76

Yang kedua akan lebih efisien karena hanya memiliki satu predikat untuk mengevaluasi terhadap setiap item dalam koleksi di mana seperti yang pertama, itu menerapkan predikat pertama untuk semua item pertama dan hasilnya (yang dipersempit pada titik ini) adalah digunakan untuk predikat kedua dan seterusnya. Hasilnya semakin dipersempit setiap lintasan tetapi masih melibatkan beberapa lintasan.

Juga chaining (metode pertama) hanya akan berfungsi jika Anda DANING predikat Anda. Sesuatu seperti ini x.Age == 10 || x.Fat == truetidak akan bekerja dengan metode pertama Anda.


1
Kondisi rantai ORing agak mungkin menggunakan ekstensi ini: albahari.com/nutshell/predicatebuilder.aspx
jahu

142

EDIT: LINQ to Objects tidak berperilaku seperti yang saya harapkan. Anda mungkin tertarik dengan posting blog yang baru saya tulis tentang ini ...


Mereka berbeda dalam hal apa yang akan disebut - yang pertama setara dengan:

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

dimana yang terakhir setara dengan:

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

Sekarang, perbedaan apa yang sebenarnya terjadi tergantung pada implementasi Wherepemanggilan. Jika penyedia berbasis SQL, saya berharap keduanya akhirnya membuat SQL yang sama. Jika ada di LINQ to Objects, yang kedua akan memiliki tingkat tipuan yang lebih sedikit (hanya akan ada dua iterator yang terlibat, bukan empat). Apakah tingkat tipuan itu penting dalam hal kecepatan adalah masalah yang berbeda.

Biasanya saya akan menggunakan beberapa whereklausa jika mereka merasa mereka mewakili kondisi yang sangat berbeda (mis. Satu harus dilakukan dengan satu bagian dari suatu objek, dan satu sama sekali terpisah) dan satu whereklausa ketika berbagai kondisi terkait erat (misalnya nilai tertentu lebih besar dari minimum dan kurang dari maksimum). Pada dasarnya ada baiknya mempertimbangkan keterbacaan sebelum ada sedikit perbedaan kinerja.


1
@ JonSkeet Mungkin saya salah, tetapi setelah ulasan singkat implementasi Linq Where, saya tidak yakin akan hal itu. Nested Where dikombinasikan dengan metode statis 'CombinePredicates'. Koleksi diulang hanya satu kali oleh satu iterator dengan predikat gabungan. Tentu saja, ada dampak kinerja menggabungkan fungsi, tetapi sangat terbatas. Apakah kamu baik-baik saja ?
Cybermaxs

@Cybermaxs: Tidak yakin apa , tepatnya? Saya tidak pernah menyarankan bahwa koleksi akan diulang lebih dari satu kali.
Jon Skeet

@ JonSkeet ya tentu saja tetapi pada akhirnya semua predikat digabungkan dan hanya satu iterator yang terlibat. Lihatlah Diumerik. Di mana Pilih Elterable.
Cybermaxs

Halaman yang Anda tautkan sedang down sekarang. Bisakah Anda memperbarui tautannya jika artikelnya masih ada di tempat lain? Terima kasih.
Asad Saeeduddin

2
@Asad: Diperbarui. (Blog saya sudah pindah.)
Jon Skeet

13

Yang pertama akan diterapkan:

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

Berbeda dengan yang lebih sederhana (dan jauh lebih cepat mungkin lebih cepat):

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

6
"Jauh lebih cepat"? Kami bahkan belum tahu implementasi LINQ mana yang terlibat, jadi sulit untuk melampirkan implikasi kinerja apa pun padanya.
Jon Skeet

Dalam kasus umum yang terakhir hanya membutuhkan 1 loop. Penyedia dapat memilih untuk meratakan contoh pertama, tetapi tidak diperlukan.
user7116

2
Memang ... tapi kau mengklaim yang terakhir adalah jauh lebih cepat. Sama sekali tidak jelas bahwa itu akan secara signifikan lebih cepat sama sekali - setelah semua, pentingnya perbedaan kinerja akan tergantung pada bagaimana ini digunakan.
Jon Skeet

1
@ Jon: tidak ada perselisihan. Seperti yang Anda perhatikan, kenyataannya bisa jadi penyedia LINQ berjalan dan melakukan transformasi optimasi yang berguna untuk ekspresi. Tetapi mengingat yang kedua hanya membutuhkan satu loop dan manfaat dari hubungan pendek boolean, sulit untuk melihat mengapa itu tidak boleh diberi label sebagai "jauh lebih cepat" secara umum. Jika OP hanya memiliki 5 elemen, poin saya bisa diperdebatkan.
user7116

11

ketika saya lari

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

dan

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

terhadap tabel Pelanggan saya menghasilkan kueri sql yang sama

-- 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)

jadi dalam terjemahan ke sql tidak ada perbedaan dan Anda sudah melihat di jawaban lain bagaimana mereka akan dikonversi ke ekspresi lambda


ok, maka Anda ingin mengatakan bahwa itu tidak akan memiliki efek kinerja jika saya menggunakan ini?
Bimal Das

WHERE klausa sebenarnya dirantai. Jadi, tidak masalah bagaimana Anda menulisnya. Tidak ada perbedaan kinerja.
hastrb

3

Melihat di bawah tenda, dua pernyataan akan diubah menjadi representasi kueri yang berbeda. Tergantung pada QueryProviderdari Collection, ini mungkin dioptimalkan atau tidak.

Ketika ini adalah panggilan linq-ke-objek, beberapa klausa akan mengarah ke rantai nomor IEnumer yang saling membaca. Menggunakan formulir satu klausa akan membantu kinerja di sini.

Ketika penyedia yang mendasarinya menerjemahkannya ke dalam pernyataan SQL, kemungkinannya bagus bahwa kedua varian akan membuat pernyataan yang sama.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.