Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Petunjuk / Saran
Baru mengembangkan perangkat lunak? Mulai dengan tutorial Memulai terlebih dahulu. Anda akan menemukan antarmuka ketika Anda perlu menentukan perilaku bersama di seluruh jenis yang tidak terkait.
Berpengalaman dalam bahasa lain? Antarmuka C# mirip dengan antarmuka dalam Java atau protokol di Swift. Lewati bagian implementasi eksplisit untuk pola C#-spesifik.
Antarmuka mendefinisikan sebuah kontrak: sekelompok metode, properti, event, dan pengindeks terkait yang harus diimplementasikan oleh class atau struct. Antarmuka memungkinkan satu jenis mengimplementasikan beberapa kontrak, yang penting karena C# tidak mendukung beberapa warisan kelas. Struktur tidak dapat mewarisi dari struct atau kelas lain, sehingga antarmuka adalah satu-satunya cara untuk menambahkan perilaku bersama di seluruh jenis struct.
Contoh berikut mendeklarasikan antarmuka dan kelas yang mengimplementasikannya:
interface IEquatable<T>
{
bool Equals(T obj);
}
public class Car : IEquatable<Car>
{
public string? Make { get; set; }
public string? Model { get; set; }
public string? Year { get; set; }
public bool Equals(Car? car) =>
car is not null &&
(Make, Model, Year) == (car.Make, car.Model, car.Year);
}
Setiap kelas atau struct yang menerapkan IEquatable<T> harus menyediakan Equals metode yang cocok dengan tanda tangan antarmuka. Anda dapat mengandalkan implementasi apa pun IEquatable<T> untuk mendukung perbandingan kesetaraan, terlepas dari jenis betonnya. Kepastian prediktif adalah nilai inti antarmuka.
Mendeklarasikan antarmuka
Tentukan antarmuka dengan interface kata kunci. Menurut konvensi, nama antarmuka dimulai dengan huruf besar I.
interface ILogger
{
void Log(string message);
string Name { get; }
}
Antarmuka dapat berisi metode, properti, peristiwa, dan pengindeks. Antarmuka tidak dapat berisi bidang instans, konstruktor instans, atau finalizer. Anggota public secara bawaan. Anda dapat menentukan pengubah aksesibilitas lain saat diperlukan. Misalnya, gunakan internal untuk anggota yang seharusnya tidak terlihat di luar assembly.
Menerapkan antarmuka
Kelas atau struktur mencantumkan antarmuka yang diterapkannya setelah titik dua dalam deklarasinya. Kelas harus memberikan implementasi untuk setiap anggota yang dideklarasikan dalam antarmuka:
public class ConsoleLogger : ILogger
{
public string Name => "Console";
public void Log(string message) =>
Console.WriteLine($"[{Name}] {message}");
}
public class FileLogger : ILogger
{
public string Name => "File";
public void Log(string message)
{
// In a real app, write to a file
Console.WriteLine($"[{Name}] Writing to file: {message}");
}
}
Kelas dapat mengimplementasikan beberapa antarmuka, dipisahkan oleh koma. Ini harus menyediakan implementasi untuk semua anggota dari setiap antarmuka yang dicantumkannya.
Implementasi eksplisit
Terkadang Anda perlu menerapkan anggota antarmuka tanpa menjadikannya bagian dari API publik kelas. Implementasi eksplisit memenuhi syarat anggota dengan nama antarmuka. Anggota hanya dapat diakses melalui variabel jenis antarmuka:
interface IMetric
{
double GetDistance(); // Returns meters
}
interface IImperial
{
double GetDistance(); // Returns feet
}
public class Runway(double meters) : IMetric, IImperial
{
// Explicit implementation for IMetric
double IMetric.GetDistance() => meters;
// Explicit implementation for IImperial
double IImperial.GetDistance() => meters * 3.28084;
}
Implementasi eksplisit berguna ketika dua antarmuka mendeklarasikan anggota dengan nama yang sama, atau ketika Anda ingin menjaga permukaan publik kelas tetap bersih. Untuk detail selengkapnya, lihat Implementasi Antarmuka Eksplisit.
Pewarisan antarmuka
Antarmuka dapat mewarisi dari satu atau lebih antarmuka lainnya. Kelas yang mengimplementasikan antarmuka turunan harus mengimplementasikan semua anggota dari antarmuka turunan dan semua antarmuka dasarnya:
interface IDrawable
{
void Draw();
}
interface IShape : IDrawable
{
double Area { get; }
}
public class Circle(double radius) : IShape
{
public double Area => Math.PI * radius * radius;
public void Draw() =>
Console.WriteLine($"Drawing circle with area {Area:F2}");
}
Kelas yang mengimplementasikan IShape dapat secara implisit dikonversi ke IDrawable, karena IShape mewarisi darinya.
Antarmuka vs. kelas abstrak
Baik antarmuka maupun kelas abstrak mendefinisikan kontrak yang harus dipenuhi jenis turunan.
- Gunakan kelas abstrak saat jenis terkait berbagi status (bidang), konstruktor, atau anggota non-publik. Kelas abstrak memungkinkan Anda mengembangkan hierarki dengan menambahkan anggota baru dengan perilaku default tanpa melanggar jenis turunan yang ada.
- Gunakan antarmuka saat tipe perlu memenuhi kontrak yang melintasi hierarki yang tidak terkait, atau ketika perlu menerapkan beberapa kontrak. Antarmuka tidak dapat mendeklarasikan bidang instans atau konstruktor, sehingga paling cocok untuk menambahkan kemampuan ke jenis yang sudah memiliki kelas dasar. Untuk skenario lanjutan, antarmuka juga mendukung implementasi anggota default.
Kelas hanya dapat mewarisi dari satu kelas dasar tetapi dapat menerapkan beberapa antarmuka. Perbedaan itu sering membuat antarmuka menjadi pilihan yang lebih baik untuk menentukan kemampuan yang memotong hierarki jenis.
Bekerja dengan antarmuka internal
Anda biasanya dapat menerapkan antarmuka internal dengan anggota publik, selama semua jenis dalam tanda tangan antarmuka dapat diakses secara publik. Saat antarmuka menggunakan jenis internal dalam tanda tangan anggotanya, Anda harus menggunakan implementasi eksplisit karena anggota yang menerapkan tidak dapat menjadi publik saat mengekspos jenis internal:
internal class InternalConfiguration
{
public string Setting { get; set; } = "";
}
internal interface ILoggable
{
void Log(string message);
}
internal interface IConfigurable
{
void Configure(InternalConfiguration config);
}
public class ServiceImplementation : ILoggable, IConfigurable
{
// Implicit implementation: ILoggable uses only public types in its signature
public void Log(string message) =>
Console.WriteLine($"Log: {message}");
// Explicit implementation: IConfigurable uses internal types
void IConfigurable.Configure(InternalConfiguration config) =>
Console.WriteLine($"Configured with: {config.Setting}");
}
Dalam contoh sebelumnya, IConfigurable menggunakan jenis InternalConfiguration internal dalam tanda tangan metodenya.
ServiceImplementation menggunakan implementasi eksplisit untuk anggota tersebut. Sebaliknya, ILoggable hanya menggunakan jenis publik (string) dalam tanda tangannya dan dapat diimplementasikan secara implisit.
Anggota antarmuka default dan anggota abstrak statis
Antarmuka mendukung dua fitur canggih yang melampaui kontrak dasar:
- Anggota antarmuka default memungkinkan antarmuka menyediakan isi metode. Menerapkan tipe mewarisi implementasi default dan dapat secara opsional meng-override-nya. Untuk informasi selengkapnya, lihat metode antarmuka default.
- Anggota abstrak statis memerlukan jenis penerapan untuk menyediakan anggota statis, yang berguna untuk menentukan kontrak operator atau pola pabrik. Untuk informasi selengkapnya, lihat anggota abstrak statis di antarmuka.
Kedua fitur dibahas dalam artikel tentang antarmuka dalam referensi bahasa. Sebagian besar penggunaan antarmuka sehari-hari melibatkan pola deklarasikan dan penerapan yang dijelaskan sebelumnya dalam artikel ini.
Ringkasan antarmuka
- Antarmuka mendefinisikan kontrak metode, properti, peristiwa, dan pengindeks.
- Kelas atau struktur yang mengimplementasikan antarmuka harus menyediakan implementasi untuk semua anggota yang dideklarasikan (kecuali antarmuka menyediakan implementasi default).
- Anda tidak dapat menginstansiasi antarmuka secara langsung.
- Kelas atau struktur dapat mengimplementasikan satu atau beberapa antarmuka. Kelas dapat mewarisi kelas dasar dan juga mengimplementasikan satu atau beberapa antarmuka.
- Nama antarmuka secara konvensional dimulai dengan
I.