Menu

Interface C#: Deklarasi, Implementasi, Default Method dan Contoh

Cara kerja interface di C#: mendeklarasikannya, mengimplementasikannya di class dan struct, memakai interface sebagai tipe, mengimplementasikan beberapa interface sekaligus, implementasi eksplisit, interface framework yang paling sering kamu implementasikan (IComparable<T>, IEnumerable<T>, IDisposable), dan default interface method dari C# 8.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

Interface adalah sebuah kontrak: daftar member bernama yang dijanjikan akan disediakan oleh sebuah tipe. Interface menyatakan apa yang bisa dilakukan sebuah tipe, tanpa menyebut bagaimana caranya. Class dan struct mengimplementasikan interface, dan kode yang ditulis untuk interface bekerja dengan setiap implementasinya, termasuk yang baru ditulis kemudian.

Mendeklarasikan dan mengimplementasikan interface

Interface mendeklarasikan member tanpa badan. Menurut kebiasaan, namanya diawali I. Sebuah class mengimplementasikannya dengan menuliskannya setelah titik dua dan menyediakan member publik untuk setiap member interface:

Output:

15% off: 68
10 off: 70
Best for 80: 68
Best for 40: 30

BestPrice tidak tahu apa-apa tentang PercentOff atau FixedOff. Ia hanya bergantung pada IDiscount, jadi menambahkan class BuyOneGetOne nanti tidak membutuhkan perubahan apa pun padanya. Pemisahan inilah alasan utama interface ada.

Beberapa aturan tentang deklarasinya:

  • Member interface secara default bersifat publik, dan member yang mengimplementasikannya juga harus publik (kecuali diimplementasikan secara eksplisit, lihat di bawah).
  • Interface bisa mendeklarasikan method, property, indexer dan event. Interface tidak bisa mendeklarasikan field instance atau constructor.
  • Class yang melewatkan sebuah member tidak bisa di-compile: error CS0535, "'FixedOff' does not implement interface member 'IDiscount.Apply(decimal)'".
  • Kamu tidak bisa membuat interface dengan new. Kamu membuat class yang mengimplementasikannya dan bisa menyimpannya di variabel bertipe interface: IDiscount d = new FixedOff(5);.

Interface sebagai tipe

Variabel, parameter, field atau tipe kembalian bisa dideklarasikan sebagai interface. Ia lalu menerima objek apa pun yang mengimplementasikannya, dan hanya memperlihatkan member interface tersebut. Dengan cara inilah kamu menulis kode yang tidak peduli implementasi mana yang diterimanya, dan dengan cara ini juga pengujian bisa menukarnya dengan versi palsu:

public class Checkout
{
    private readonly IPaymentGateway gateway;          // not StripeGateway, not PayPalGateway
    public Checkout(IPaymentGateway gateway) { this.gateway = gateway; }

    public bool Pay(decimal amount) => gateway.Charge(amount);
}

// Production: new Checkout(new StripeGateway(apiKey))
// Unit test:  new Checkout(new FakeGateway(alwaysSucceeds: true))

Container dependency injection di ASP.NET Core dibangun di atas ini: service didaftarkan dan diminta berdasarkan interface-nya.

Mengimplementasikan beberapa interface

Sebuah class punya paling banyak satu base class, tapi bisa mengimplementasikan interface sebanyak apa pun. Base class ditulis pertama, lalu interface-nya, dipisahkan dengan koma:

Output:

INVOICE Studio rent March: 950.00
True
True

Interface juga bisa mewarisi interface lain: interface IRepository<T> : IReadRepository<T> menambahkan member pada member yang diwarisinya, dan class yang mengimplementasikan IRepository<T> harus menyediakan kedua kumpulan member itu.

Implementasi interface secara eksplisit

Kadang dua interface mendeklarasikan member dengan nama yang sama tapi maknanya berbeda, atau sebuah member interface tidak masuk akal ada di tampilan publik class itu sendiri. Implementasikan secara eksplisit dengan menambahkan nama interface di depannya dan tanpa access modifier:

Output:

Desk lamp,34.90
Desk lamp for 34.90

Member yang diimplementasikan secara eksplisit hanya bisa diakses lewat interface. Framework memakai cara ini untuk menjauhkan hal yang mengganggu dari tipe-tipe umum: array mengimplementasikan IList.Add secara eksplisit (method itu melempar NotSupportedException, karena ukuran array tetap), sehingga method itu tidak muncul saat kamu mengetik array..

Interface framework yang akan kamu implementasikan

Mengimplementasikan interface standar menghubungkan tipemu dengan kode framework yang sudah ada. Ada tiga yang terus-menerus muncul.

IComparable<T> memberi sebuah tipe urutan alaminya, yang dipakai oleh List<T>.Sort(), Array.Sort dan Max(). CompareTo mengembalikan angka negatif, nol atau angka positif:

Output:

1.4, 2.9, 2.10, 10.0

Mengurutkan versi yang sama sebagai string akan menghasilkan 1.4, 10.0, 2.10, 2.9. Tanpa IComparable<T>, Sort() melempar InvalidOperationException karena tidak punya cara untuk membandingkan dua objek Version.

IEnumerable<T> membuat sebuah tipe bisa dipakai di foreach dan dengan LINQ. Cara mudah mengimplementasikan GetEnumerator adalah dengan yield return, yang dibahas di halaman IEnumerable dan yield.

IDisposable menandai tipe yang memegang sesuatu yang harus dilepaskan (file handle, koneksi, timer). Satu-satunya method-nya, Dispose, adalah yang dipanggil pernyataan using saat bloknya berakhir, bahkan saat exception dilempar:

Output:

open sales.txt
  write to sales.txt: March total: 12400
close sales.txt
after using

Default interface method (C# 8)

Dulu, menambahkan member ke interface yang sudah dipublikasikan akan merusak setiap class yang mengimplementasikannya. Sejak C# 8, member interface boleh punya badan, yang diwarisi oleh class pengimplementasinya kecuali class itu menyediakan versinya sendiri:

public interface ILogger
{
    void Write(string message);

    // New in version 2 of the library. Existing implementers keep compiling.
    void Error(string message) => Write("ERROR: " + message);
}

public class ConsoleLogger : ILogger
{
    public void Write(string message) => Console.WriteLine(message);
}

ILogger log = new ConsoleLogger();
log.Error("disk full");            // ERROR: disk full

var direct = new ConsoleLogger();
// direct.Error("x");              // does not compile: the default method belongs to the interface

Baris terakhir adalah bagian yang mengejutkan banyak orang: default method tidak diwariskan menjadi member class itu sendiri, jadi hanya bisa dipanggil lewat tipe interface. Default method juga tidak bisa memakai field instance (interface tidak punya field); method ini bekerja lewat member interface lainnya. Fitur ini butuh .NET Core 3.0 ke atas dan tidak tersedia di .NET Framework.

C# 8 juga mengizinkan member static di interface, dan C# 11 menambahkan member static abstract, yang membuat kode generik bisa memanggil method static atau operator pada T. Itulah dasar generic math di .NET 7 (INumber<T>, where T : INumber<T>).

Interface atau abstract class?

Pakai interface untuk kemampuan yang bisa dimiliki bersama oleh tipe-tipe yang tidak berhubungan, saat struct juga harus ikut serta, atau saat sebuah class butuh beberapa peran seperti itu. Pakai abstract class saat implementasinya adalah variasi dari satu hal yang sama dan berbagi state atau algoritma yang tetap. Halaman abstract class punya tabel perbandingan keduanya. Banyak desain memakai keduanya: interface untuk pemakai, dan abstract base class yang mengimplementasikan bagian-bagian yang membosankan untuk para pengimplementasi.

Kesalahan yang sering terjadi

  • Implementasi yang hilang atau tidak publik. Setiap member interface butuh member publik dengan signature yang cocok, atau implementasi eksplisit. Kalau tidak, muncul CS0535 (atau CS0737 saat method-nya ada tapi tidak publik).
  • Interface dengan satu implementasi "untuk jaga-jaga". Interface layak ada saat ada beberapa implementasi atau versi palsu untuk pengujian. Kalau tidak, itu hanya satu file lagi yang harus ditelusuri.
  • Interface yang gemuk. Interface dengan dua puluh member memaksa setiap pengimplementasi menulis dua puluh member. Pecah menjadi peran-peran yang lebih kecil (IReader, IWriter) yang bisa digabungkan oleh class.
  • Melakukan cast dari interface kembali ke class. ((StripeGateway)gateway).Refund() membatalkan pemisahannya. Kalau pemanggil butuh Refund, method itu seharusnya ada di interface.
  • Mengira default interface method bisa dipanggil lewat class. Method itu diakses lewat tipe interface.

Pertanyaan yang sering diajukan

Apa itu interface di C#?

Interface adalah kumpulan member bernama (method, property, event, indexer) yang dijanjikan akan disediakan oleh sebuah tipe, tanpa state instance. interface IDiscount { decimal Apply(decimal price); } menyatakan "apa pun yang merupakan IDiscount bisa menerapkan dirinya pada sebuah harga". Class dan struct mengimplementasikannya dengan titik dua, lalu kode bisa bekerja dengan salah satu dari mereka lewat tipe interface itu.

Bagaimana cara mengimplementasikan interface di C#?

Tulis interface itu setelah titik dua di deklarasi class (setelah base class, kalau ada) dan sediakan member publik untuk setiap member interface: class HolidayDiscount : IDiscount { public decimal Apply(decimal price) => price * 0.9m; }. Member yang terlewat adalah error CS0535. Visual Studio dan Rider bisa membuatkan kerangkanya lewat quick fix.

Apakah class C# bisa mengimplementasikan banyak interface?

Bisa, sebanyak yang dibutuhkan, dipisahkan dengan koma: class Invoice : IPrintable, IComparable<Invoice>, IDisposable. Inilah jawaban C# untuk multiple inheritance: sebuah class punya satu base class tapi bisa memainkan banyak peran. Kalau dua interface mendeklarasikan member yang sama, satu method publik bisa memenuhi keduanya, atau kamu bisa memakai implementasi eksplisit untuk memberi masing-masing versinya sendiri.

Apa itu explicit interface implementation di C#?

Mengimplementasikan sebuah member dengan nama interface di depannya dan tanpa access modifier: string IExportable.Format() { ... }. Member itu kemudian hanya bisa dipanggil lewat variabel bertipe interface, bukan lewat class-nya. Cara ini dipakai untuk mengatasi bentrok nama antara dua interface dan untuk menjauhkan member interface yang jarang dipakai dari tampilan publik class.

Apa itu default interface method di C#?

Sejak C# 8, sebuah member interface boleh punya badan: void Error(string m) => Write("ERROR: " + m);. Class yang mengimplementasikannya mendapatkannya secara gratis dan boleh menyediakan versinya sendiri. Fitur ini ada supaya pembuat library bisa menambahkan member ke interface yang sudah dipublikasikan tanpa merusak setiap class yang mengimplementasikannya. Default method diakses lewat tipe interface, bukan lewat class.

Kenapa nama interface C# diawali huruf I?

Itu konvensi penamaan .NET (IEnumerable, IDisposable, IComparable<T>): huruf I kapital diikuti nama dalam PascalCase, sering kali kata sifat berakhiran "-able". Compiler tidak mewajibkannya, tapi mengikutinya membuat tipe interface langsung dikenali, terutama di deklarasi class tempat base class dan interface berada dalam satu daftar.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai