LINQ (Language Integrated Query) adalah library berisi method untuk memfilter, mengubah, mengurutkan, mengelompokkan dan merangkum urutan data. Tambahkan using System.Linq; dan setiap array, List<T>, Dictionary serta IEnumerable<T> lainnya mendapat method seperti Where, Select dan OrderBy, yang masing-masing menerima lambda yang menyatakan apa yang harus dilakukan pada satu elemen.
Query pertama: method syntax dan query syntax
LINQ punya dua cara penulisan. Method syntax merangkai pemanggilan; query syntax terbaca seperti SQL. Compiler mengubah query syntax menjadi pemanggilan method yang sama, jadi hasilnya identik.
Output:
95, 91, 88
95, 91, 88
Method syntax lebih umum di kode modern, dan sebagian operator (Take, Distinct, Count, First, ToList) hanya ada dalam bentuk method. Query syntax unggul untuk join dan untuk query yang memperkenalkan variabel perantara dengan let. Bagian selanjutnya di halaman ini memakai method syntax dan menunjukkan query syntax saat bentuk itu menambahkan sesuatu.
Where dan Select: memfilter dan memproyeksikan
Where mempertahankan elemen yang cocok dengan sebuah kondisi. Select mengubah setiap elemen menjadi sesuatu yang lain. Bersama-sama, keduanya mencakup sebagian besar query sehari-hari.
Output:
Mug, Kettle
MUG $8.50 | LAMP $32.00 | KETTLE $27.00 | RUG $120.00
Lamp: 38.40
Rug: 144.00
1. Mug 2. Lamp 3. Kettle 4. Rug
Where tidak pernah mengubah elemen, dan Select tidak pernah mengubah jumlahnya. new { p.Name, WithTax = ... } membuat anonymous type, yaitu class yang ditulis compiler untukmu, dan property-nya kamu baca dengan sintaks titik biasa. Tipe ini tidak punya nama yang bisa kamu tulis, jadi variabel yang menyimpannya harus dideklarasikan dengan var, dan tipe ini cocok untuk hasil yang tetap berada di dalam satu method.
OrderBy, ThenBy dan mengurutkan dengan beberapa key
Output:
Ana 60
Ana 15
Ben 90
Ben 40
Chloe 25
Ben, Ana, Ben, Chloe, Ana
OrderBy bersifat stabil: elemen dengan key yang sama tetap dalam urutan aslinya. Pakai ThenBy atau ThenByDescending untuk key kedua. OrderBy kedua akan mengurutkan ulang semuanya berdasarkan key-nya sendiri, sehingga key pertama hanya menjadi penentu saat nilainya sama: prioritas yang terbalik. Di query syntax, key-nya dipisahkan dengan koma: orderby o.Customer, o.Total descending.
Tidak seperti List<T>.Sort, OrderBy tidak mengubah sumbernya dan mengembalikan urutan baru.
First, Single, Last dan versi OrDefault-nya
Method-method ini mengembalikan satu elemen, bukan sebuah urutan. Versi biasanya melempar exception saat tidak ada yang bisa dikembalikan; versi OrDefault mengembalikan nilai default tipe tersebut.
Output:
Ana
Ben
Bea
True
First: InvalidOperationException
Single: InvalidOperationException
0
Exception dari First pada urutan kosong adalah InvalidOperationException dengan pesan "Sequence contains no elements" (atau "Sequence contains no matching element" saat predicate tidak cocok dengan apa pun), salah satu error C# yang paling sering dicari. Pakai FirstOrDefault saat "tidak ada" adalah hasil yang wajar, lalu periksa apakah hasilnya null.
Baris terakhir menunjukkan jebakan value type: untuk urutan int, FirstOrDefault mengembalikan 0 saat tidak ada yang cocok, dan itu tidak bisa dibedakan dari 0 yang sungguhan. Filter dengan Where lalu periksa Any() terlebih dahulu, atau di .NET 6 ke atas kirim nilai default-mu sendiri: scores.FirstOrDefault(s => s > 100, -1).
Single dipakai untuk "tepat satu": method ini melempar exception kalau tidak ada elemen atau ada lebih dari satu, sehingga ia berfungsi sebagai pernyataan pemastian sekaligus query.
Any, All, Count dan Contains
Output:
True
True
True
3
True
True
Any() berhenti di kecocokan pertama, jadi items.Any() adalah cara yang tepat untuk bertanya "apakah ada isinya?". items.Count() > 0 membaca property Count saat sumbernya adalah koleksi seperti list atau array, tapi pada query atau urutan lazy lainnya, ia menelusuri setiap elemen terlebih dahulu. All pada urutan kosong mengembalikan true, karena tidak ada elemen yang gagal memenuhi kondisinya.
Sum, Average, Min dan Max
Output:
Total: 28.25
Items: 7
Priciest: 7.25
Average price: 4.92
0
Average: InvalidOperationException
Sum dari urutan kosong adalah 0, tapi Average, Min dan Max dari urutan angka yang kosong melempar InvalidOperationException: tidak ada jawaban yang masuk akal. Lindungi dengan Any(), atau pakai DefaultIfEmpty() terlebih dahulu: empty.DefaultIfEmpty(0).Average().
Distinct, Take dan Skip
Output:
home, shop, cart
Page 1: 1 2 3 4 5 6 7 8 9 10
Page 2: 11 12 13 14 15 16 17 18 19 20
Page 3: 21 22 23
12, 15
Distinct mempertahankan kemunculan pertama setiap nilai sesuai urutannya. Skip(n).Take(m) adalah pola standar untuk pembagian halaman, dan TakeWhile/SkipWhile berhenti atau mulai di elemen pertama yang gagal memenuhi sebuah kondisi. Enumerable.Range(start, count) menghasilkan urutan bilangan bulat tanpa array.
GroupBy
GroupBy memilah elemen ke dalam beberapa wadah berdasarkan sebuah key. Setiap grup punya Key dan juga merupakan sebuah urutan, jadi kamu bisa melakukan agregasi padanya.
Output:
North: 3 sales, total 600
South: 2 sales, total 400
East: 1 sales, total 350
Top rep: Ana 400
North -> Ana/Chloe/Ana; South -> Ben/Eli; East -> Dev
Grup-grup itu keluar sesuai urutan kemunculan pertama key-nya di sumber (North, South, East), tidak terurut; tambahkan OrderBy(g => g.Key) kalau kamu membutuhkannya. Di query syntax, group X by K memilih apa yang masuk ke setiap grup (di sini hanya nama rep-nya) dan into g memberi nama grup itu supaya query-nya bisa dilanjutkan.
ToList, ToArray dan ToDictionary
Method To... menjalankan query lalu menyimpan hasilnya.
Output:
ben@x.com
2
ToDictionary: ArgumentException
ToDictionary melempar ArgumentException saat dua elemen menghasilkan key yang sama, persis seperti Dictionary.Add. Kalau duplikat memang mungkin muncul, kelompokkan terlebih dahulu (seperti yang dilakukan wordCounts) atau pakai ToLookup, yang memetakan setiap key ke sebuah urutan nilai.
Deferred execution
Where, Select, OrderBy, GroupBy dan sebagian besar operator lainnya mengembalikan objek query dan tidak mengerjakan apa pun sampai ada yang mengenumerasinya. Ada dua akibat yang perlu kamu lihat:
Output:
25, 40, 99
converting 10
converting 25
converting 40
converting 99
Count: 4
converting 10
converting 25
converting 40
converting 99
Max: 9900
converting 10
converting 25
converting 40
converting 99
Cached: 4, 9900
Query pertama melihat 99 walaupun nilai itu ditambahkan setelah query ditulis: filter-nya berjalan saat string.Join mengenumerasinya. Query kedua mencetak proses konversinya dua kali karena Count() dan Max() masing-masing mengenumerasinya dari awal. Dengan lambda yang ringan, itu hanya pekerjaan yang sia-sia; dengan query database, pembacaan file, atau angka acak di dalam lambda, kedua kali jalan itu bisa mengembalikan data yang berbeda. Saat kamu akan memakai sebuah hasil lebih dari sekali, wujudkan hasilnya dengan ToList() atau ToArray().
Operator yang mengembalikan satu nilai (Count, Sum, First, Any, Max) dan method To... langsung dijalankan. Lihat IEnumerable dan yield untuk cara sifat lazy itu dibangun.
Referensi singkat
| Operator | Fungsinya | Mengembalikan |
|---|---|---|
Where(x => cond) | Memfilter | Urutan (ditunda) |
Select(x => expr) | Mengubah setiap elemen | Urutan (ditunda) |
SelectMany(x => seq) | Meratakan urutan bersarang | Urutan (ditunda) |
OrderBy, ThenBy (+Descending) | Mengurutkan, stabil | Urutan (ditunda) |
GroupBy(x => key) | Mengelompokkan berdasarkan key | Urutan berisi grup (ditunda) |
Distinct, Take, Skip | Menghapus duplikat, membagi halaman | Urutan (ditunda) |
First, Single, Last | Satu elemen, melempar exception kalau tidak ada | Elemen |
FirstOrDefault dan sejenisnya | Satu elemen atau nilai default | Elemen |
Any, All, Contains | Memeriksa | bool |
Count, Sum, Min, Max, Average | Agregasi | Angka |
ToList, ToArray, ToDictionary | Menjalankan dan menyimpan | Koleksi |
Kesalahan yang sering terjadi
First()pada urutan yang bisa kosong. Melempar "Sequence contains no elements"; pakaiFirstOrDefaultlalu periksa hasilnya.- Dua pemanggilan
OrderBy. Key kedua menjadi key utama; pakaiThenBy. - Mengenumerasi sebuah query beberapa kali. Pekerjaannya diulang; panggil
ToList()sekali. - Mengira query menyimpan salinan sumbernya. Query membaca sumbernya saat dienumerasi, bukan saat ditulis.
Average,Min,Maxpada urutan kosong. Ketiganya melempar exception; lindungi denganAny()atauDefaultIfEmpty.Count() > 0untuk memeriksa apakah ada elemen.Any()berhenti di elemen pertama.
Pertanyaan yang sering diajukan
Apa itu LINQ di C#?
LINQ (Language Integrated Query) adalah kumpulan extension method di System.Linq, seperti Where, Select, OrderBy dan GroupBy, yang bekerja pada IEnumerable<T> apa pun: array, list, dictionary, dan hasil dari query lain. C# juga punya query syntax (from x in items where ... select ...) yang diterjemahkan compiler menjadi pemanggilan method yang sama.
Apa perbedaan Select dan Where di LINQ?
Where memfilter: ia mempertahankan elemen yang membuat lambda mengembalikan true dan tidak mengubah elemen itu. Select memproyeksikan: ia mengubah setiap elemen menjadi sesuatu yang lain, seperti sebuah property atau objek baru, dan jumlah elemennya tetap sama. Keduanya sering dirangkai: people.Where(p => p.Age >= 18).Select(p => p.Name).
Apa perbedaan First dan FirstOrDefault?
First() mengembalikan elemen pertama (atau elemen pertama yang cocok dengan predicate) dan melempar InvalidOperationException ("Sequence contains no elements" atau "no matching element") saat tidak ada elemen. FirstOrDefault() justru mengembalikan nilai default tipe tersebut: null untuk class dan 0 untuk int, jadi periksa hasilnya sebelum memakainya.
Bagaimana cara mengurutkan berdasarkan dua field dengan LINQ?
Rangkai ThenBy setelah OrderBy: orders.OrderBy(o => o.Customer).ThenByDescending(o => o.Total). Memanggil OrderBy dua kali tidak berhasil: pemanggilan kedua mengurutkan ulang seluruh urutan, jadi key-nya menjadi key utama dan key pertama hanya menjadi penentu saat nilainya sama, kebalikan dari yang dimaksud.
Bagaimana cara kerja GroupBy di LINQ?
GroupBy(x => key) mengembalikan satu grup untuk setiap key yang berbeda. Setiap grup adalah IGrouping<TKey, TElement>: ia punya property Key dan juga merupakan urutan berisi elemen-elemen dengan key itu, jadi kamu bisa memanggil Count(), Sum() atau Select padanya. Grup-grup itu keluar sesuai urutan kemunculan pertama key-nya.
Apa itu deferred execution di LINQ?
Sebagian besar operator LINQ tidak berjalan saat kamu memanggilnya; operator itu mengembalikan query yang dijalankan setiap kali dienumerasi (oleh foreach, ToList(), Count(), dan sebagainya). Karena itu, sebuah query melihat perubahan pada sumbernya yang dibuat setelah query itu didefinisikan, dan mengenumerasinya dua kali berarti mengerjakannya dua kali. Panggil ToList() atau ToArray() untuk menjalankannya sekali dan menyimpan hasilnya.