Extension method menambahkan sebuah method ke tipe yang sudah ada tanpa mengubahnya, mewarisinya, atau membungkusnya. Kamu menulis method static biasa, menandai parameter pertamanya dengan this, dan pemanggil memakainya dengan sintaks titik seolah-olah tipe itu sudah punya method tersebut sejak awal:
Output:
Extension methods let yo...
12
short
Extension...
title.Truncate(24) di-compile persis menjadi StringExtensions.Truncate(title, 24). Sintaks titik hanyalah kemudahan: method-nya tetap static, tetap berada di StringExtensions, dan hanya bisa memakai member public milik string.
Aturan untuk mendeklarasikannya
- Method-nya harus
staticdan berada di class yangstatic, tidak generic, dan tidak bersarang di dalam class lain. Class yang tidak static atau generic menghasilkan CS1106,Extension method must be defined in a non-generic static class; class yang bersarang menghasilkan CS1109,Extension methods must be defined in a top level static class. thishanya ditaruh di parameter pertama, dan tipe parameter itu adalah tipe yang diperluas. Parameter berikutnya biasa saja.- Method-nya sendiri boleh generic (
this IEnumerable<T> source), meskipun class-nya tidak boleh. - Method ini hanya melihat apa yang bisa dilihat kode luar mana pun: member public (dan, di assembly yang sama, member internal). Field private tetap private.
Konvensinya adalah memberi nama class sesuai apa yang diperluasnya (StringExtensions, EnumerableExtensions) dan mengelompokkannya di namespace seperti MyApp.Extensions.
Memperluas interface dan IEnumerable<T>
Memperluas sebuah interface menambahkan method-nya ke setiap tipe yang mengimplementasikannya. Karena itu, memperluas IEnumerable<T> memberi method tersebut sekaligus ke array, list, set, koleksi key dictionary, dan hasil query LINQ:
Output:
9.5
50
65
f0 f3 f6
EveryNth bersifat generic: T disimpulkan dari receiver-nya, jadi method ini berfungsi untuk string di sini dan untuk tipe elemen apa pun. Karena memakai yield return, method ini lazy seperti operator LINQ bawaan dan bisa dirangkai bersamanya.
LINQ dibangun dari extension method
Where, Select, OrderBy, Sum, First, dan operator LINQ lainnya adalah extension method pada IEnumerable<T>, yang didefinisikan di class static System.Linq.Enumerable. Itulah sebabnya using System.Linq; harus ada di bagian atas file sebelum list.Where(...) bisa di-compile, dan kenapa LINQ berfungsi di setiap tipe koleksi tanpa satu pun dari tipe itu mengimplementasikan method Where:
Output:
72, 88, 95
72, 88, 95
Kedua baris itu kodenya sama. Sintaks extension mengubah panggilan bersarang yang dibaca dari dalam ke luar menjadi rangkaian yang dibaca sesuai urutan langkahnya, dan inilah alasan utama extension method ada: fitur ini ditambahkan di C# 3 bersamaan dengan LINQ. Halaman LINQ membahas operator-operatornya.
Namespace-nya harus diimpor
Extension method hanya berada dalam jangkauan saat namespace class static-nya diimpor dengan using. Tanpa itu, panggilannya gagal seolah-olah method-nya tidak ada:
error CS1061: 'string' does not contain a definition for 'Truncate' and no accessible extension method 'Truncate' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?)
Tambahkan using untuk namespace extension-nya (atau, sejak C# 10, global using sekali untuk seluruh project):
Output:
EUR 49.90
Sisi sebaliknya juga berguna: extension method yang tidak kamu inginkan di mana-mana bisa ditaruh di namespace yang hanya diimpor oleh sebagian file.
Method instance selalu menang
Saat sebuah tipe sudah punya method instance yang cocok, compiler memakainya dan tidak pernah mempertimbangkan extension-nya. Extension dengan signature yang sama menjadi kode mati di setiap tempat pemanggilan yang memakai sintaks titik:
Output:
Invoice (instance method): 120
Invoice (extension): 120 USD
extension: only reachable as a static call
Extension dengan satu argumen kalah dari method instance; yang dengan dua argumen dipakai karena tidak ada method instance yang menerima string. Artinya juga, pembaruan library yang menambahkan method instance dengan nama yang sama seperti extension-mu akan diam-diam mengalihkan panggilanmu ke versi milik library. Pilih nama extension yang kecil kemungkinannya bentrok.
Memanggil pada null
Karena panggilannya sebenarnya pemanggilan static, receiver null tidak melempar exception di tempat pemanggilan. Method-nya menerima null sebagai argumen pertama dan menentukan apa artinya:
Output:
True
AL
ArgumentNullException: fullName
missing.IsBlank() yang mengembalikan true memang praktis, tetapi bisa mengejutkan pembaca yang mengira pemanggilan member pada null akan melempar exception. Terima null hanya di method yang namanya membuat hal itu jelas (IsBlank, OrEmpty); di tempat lain, lempar ArgumentNullException seperti yang dilakukan LINQ.
Memperluas enum
Enum tidak bisa punya method sendiri, jadi enum adalah sasaran yang wajar:
enum OrderStatus { Pending, Paid, Shipped, Delivered, Cancelled }
static class OrderStatusExtensions
{
public static bool IsFinal(this OrderStatus s) =>
s == OrderStatus.Delivered || s == OrderStatus.Cancelled;
}
// usage: if (order.Status.IsFinal()) { ... }
Extension member di C# 14
Sampai C# 14, hanya method yang bisa menjadi extension. C# 14 menambahkan blok extension, yang mengelompokkan member untuk satu receiver dan juga mengizinkan extension property dan member static:
// C# 14 and later
public static class StringExtensions
{
extension(string s)
{
public bool IsBlank => string.IsNullOrWhiteSpace(s); // extension property
public string Truncate(int max) => s.Length <= max ? s : s[..max] + "...";
}
}
Extension method klasik dengan parameter this tetap valid, dan itulah yang dipakai hampir semua kode dan library yang sudah ada.
Kapan sebaiknya menulisnya
Extension method cocok saat kamu tidak memiliki tipenya (string, DateTime, interface milik framework), saat kamu ingin helper yang sama ada di setiap implementasi sebuah interface, atau saat rangkaian panggilan lebih enak dibaca daripada panggilan bersarang. Saat class-nya milikmu sendiri, tambahkan method instance yang sebenarnya: method itu bisa memakai state private dan muncul di tempat pembaca mencarinya. Hindari juga memperluas object, karena method-mu akan muncul di daftar pelengkapan otomatis untuk setiap nilai di program.
Pertanyaan yang sering diajukan
Apa itu extension method di C#?
Method static yang bisa dipanggil seolah-olah method instance dari tipe lain. Kamu menulisnya di dalam class static dan menaruh this sebelum parameter pertamanya: public static bool IsBlank(this string s) => string.IsNullOrWhiteSpace(s);. Setelah itu name.IsBlank() bisa dipakai pada string apa pun, tanpa mengubah atau mewarisi string.
Kenapa extension method saya tidak ditemukan?
Extension method hanya terlihat saat namespace-nya diimpor. Kalau class static-nya ada di MyApp.Extensions, tambahkan using MyApp.Extensions; ke file yang memanggilnya; kalau tidak, compiler melaporkan CS1061, yang menyatakan bahwa tipe itu tidak punya definisi maupun extension method yang bisa diakses dengan nama tersebut. Cek juga apakah class-nya static, tidak generic, dan tidak bersarang.
Apakah extension method bisa meng-override method instance?
Tidak. Compiler mencari method instance yang cocok lebih dulu dan baru mempertimbangkan extension method kalau tidak ada. Extension dengan nama dan parameter yang sama dengan method instance diam-diam diabaikan di setiap tempat pemanggilan yang memakai sintaks instance.
Apakah extension method bisa dipanggil pada null di C#?
Bisa. Panggilannya di-compile menjadi pemanggilan method static dengan null sebagai argumen pertama, jadi tidak ada NullReferenceException yang dilempar di panggilan itu sendiri. Method-nya yang memutuskan apa yang dilakukan: menganggap null sebagai input yang valid (seperti helper bergaya IsNullOrEmpty) atau melempar ArgumentNullException, seperti yang dilakukan method LINQ.
Apakah ada extension property di C#?
Tidak ada sebelum C# 14. Sampai versi itu, hanya method yang bisa menjadi extension, jadi sesuatu yang seharusnya property ditulis sebagai method (GetFullName()). C# 14 menambahkan extension block, yang bisa mendeklarasikan extension property dan member static selain method.