Selama ini lambda kita eksekusi. Ada saat lain ketika kita justru ingin membaca lambda: kerangka ORM menerjemahkan kriteria harga > 50000 menjadi SQL, dan library validasi memeriksa nama properti yang disebut lambda. Untuk kebutuhan itu, C# menyediakan Expression<TDelegate>: tipe yang membuat compiler membangun lambda menjadi pohon node yang dapat kita telusuri, bukan menjadi kode yang langsung berjalan.
Perbedaannya hanya ada pada tipe target penugasan. Func<int, bool> menyuruh compiler menghasilkan IL, sedangkan Expression<Func<int, bool>> menyuruh compiler membangun struktur data pohon. Tipe pohon menyediakan properti Parameters dan Body untuk penelusuran, serta method Compile() untuk kembali menjadi delegate yang dapat dipanggil.
Lambda sebagai Pohon Node #
Expression<Func<int, bool>> kriteria = harga => harga > 50000;
Console.WriteLine($"Parameter : {kriteria.Parameters[0].Name}");
Console.WriteLine($"Body : {kriteria.Body.NodeType}");
BinaryExpression perbandingan = (BinaryExpression)kriteria.Body;
Console.WriteLine($"Kiri : {((ParameterExpression)perbandingan.Left).Name}");
Console.WriteLine($"Kanan : {((ConstantExpression)perbandingan.Right).Value}");
Func<int, bool> delegat = kriteria.Compile();
Console.WriteLine($"Cek 75000 : {delegat(75000)}");
Console.WriteLine($"Cek 32000 : {delegat(32000)}");Output yang Dihasilkan:
Parameter : harga
Body : GreaterThan
Kiri : harga
Kanan : 50000
Cek 75000 : True
Cek 32000 : FalseStruktur yang dibaca kode di atas dapat digambarkan sebagai pohon berikut:
Lambda harga => harga > 50000
└─ BinaryExpression NodeType: GreaterThan
├─ Kiri : ParameterExpression "harga"
└─ Kanan : ConstantExpression 50000Bedah Kode Baris per Baris #
Mari kita telaah kode di atas:
- Baris 1: Menambahkan
Expression<...>di depan tipe delegate membuat compiler menghasilkan pohon node, bukan method terkompilasi. Satu perubahan tipe ini saja yang membedakannya dari bab-bab sebelumnya. - Baris 3-4: Koleksi
Parametersmenampung parameter lambda, danBody.NodeTypemenyebut jenis operasi akar, yaituGreaterThan. - Baris 6-8: Body di-cast ke
BinaryExpressionkarena operasi perbandingan selalu memiliki dua sisi:Leftbertipe parameter danRightbertipe konstanta. - Baris 10-12:
Compile()mengubah pohon kembali menjadiFunc<int, bool>yang benar-benar dapat dipanggil, terbukti dari hasilTruedanFalsedi output.
Class ExpressionVisitor dapat menjelajahi pohon yang lebih dalam tanpa mengubah node-nya, dan provider LINQ memakai teknik tersebut untuk menerjemahkan kriteria ke bahasa lain. Sebagai fondasi, kemampuan membaca node pada contoh di atas sudah mencakup inti idenya.