Jadi, apa sebenarnya kasus penggunaan yang baik untuk mengimplementasikan antarmuka secara eksplisit?
Apakah hanya agar orang yang menggunakan kelas tidak perlu melihat semua metode / properti tersebut di intellisense?
Jadi, apa sebenarnya kasus penggunaan yang baik untuk mengimplementasikan antarmuka secara eksplisit?
Apakah hanya agar orang yang menggunakan kelas tidak perlu melihat semua metode / properti tersebut di intellisense?
Jawaban:
Jika Anda mengimplementasikan dua antarmuka, dengan metode yang sama dan implementasi yang berbeda, Anda harus mengimplementasikan secara eksplisit.
public interface IDoItFast
{
void Go();
}
public interface IDoItSlow
{
void Go();
}
public class JustDoIt : IDoItFast, IDoItSlow
{
void IDoItFast.Go()
{
}
void IDoItSlow.Go()
{
}
}
Ini berguna untuk menyembunyikan anggota yang tidak disukai. Misalnya, jika Anda menerapkan keduanya IComparable<T>
dan IComparable
biasanya lebih baik menyembunyikan IComparable
kelebihan beban agar tidak memberi kesan kepada orang-orang bahwa Anda dapat membandingkan objek dari jenis yang berbeda. Demikian pula, beberapa antarmuka tidak sesuai dengan CLS, misalnya IConvertible
, jadi jika Anda tidak mengimplementasikan antarmuka secara eksplisit, pengguna akhir bahasa yang memerlukan kepatuhan CLS tidak dapat menggunakan objek Anda. (Yang akan sangat berbahaya jika pelaksana BCL tidak menyembunyikan anggota IConvertible dari primitif :))
Catatan menarik lainnya adalah bahwa biasanya menggunakan konstruksi seperti itu berarti struct yang secara eksplisit mengimplementasikan antarmuka hanya dapat memanggilnya dengan meninju ke tipe antarmuka. Anda bisa menyiasatinya dengan menggunakan batasan umum:
void SomeMethod<T>(T obj) where T:IConvertible
Tidak akan mengemas int saat Anda memberikannya.
string
sudah ada sebelum obat generik dan praktik ini populer. Ketika .net 2 muncul, mereka tidak ingin merusak antarmuka publik string
sehingga mereka membiarkannya seperti itu dengan pengamanan di tempatnya.
Beberapa alasan tambahan untuk mengimplementasikan antarmuka secara eksplisit:
kompatibilitas mundur : Jika ICloneable
antarmuka berubah, anggota kelas yang menerapkan metode tidak perlu mengubah tanda tangan metode mereka.
kode pembersih : akan ada kesalahan kompiler jika Clone
metode tersebut dihapus dari ICloneable, namun jika Anda menerapkan metode secara implisit, Anda dapat berakhir dengan metode publik 'orphaned' yang tidak terpakai
pengetikan yang kuat : Untuk mengilustrasikan cerita supercat dengan sebuah contoh, ini akan menjadi kode sampel pilihan saya, menerapkan ICloneable
secara eksplisit memungkinkan Clone()
untuk diketik dengan kuat ketika Anda memanggilnya secara langsung sebagai anggota MyObject
contoh:
public class MyObject : ICloneable
{
public MyObject Clone()
{
// my cloning logic;
}
object ICloneable.Clone()
{
return this.Clone();
}
}
interface ICloneable<out T> { T Clone(); T self {get;} }
. Perhatikan bahwa tidak ada ICloneable<T>
batasan pada T. Sementara sebuah objek umumnya hanya dapat dikloning dengan aman jika basisnya bisa, seseorang mungkin ingin mendapatkan dari kelas dasar yang dapat dengan aman mengkloning objek dari kelas yang tidak bisa. Untuk memungkinkan itu, saya merekomendasikan agar kelas yang diwariskan mengekspos metode klon publik. Sebaliknya memiliki kelas yang diwariskan dengan protected
metode kloning dan kelas tertutup yang berasal dari mereka dan mengekspos kloning publik.
Teknik lain yang berguna adalah membuat implementasi publik suatu fungsi dari suatu metode mengembalikan nilai yang lebih spesifik daripada yang ditentukan dalam sebuah antarmuka.
Misalnya, sebuah objek dapat diimplementasikan ICloneable
, tetapi masih memiliki Clone
metode yang terlihat secara publik mengembalikan tipenya sendiri.
Demikian juga, IAutomobileFactory
mungkin memiliki Manufacture
metode yang mengembalikan Automobile
, tetapi a FordExplorerFactory
, yang mengimplementasikan IAutomobileFactory
, mungkin memiliki Manufacture
metode mengembalikan a FordExplorer
(yang berasal dari Automobile
). Kode yang mengetahui bahwa ia memiliki FordExplorerFactory
dapat menggunakan FordExplorer
properti -specific pada suatu objek yang dikembalikan oleh a FordExplorerFactory
tanpa harus melakukan typecast, sedangkan kode yang hanya mengetahui bahwa ia memiliki beberapa jenis IAutomobileFactory
akan menangani pengembaliannya sebagai file Automobile
.
Ini juga berguna ketika Anda memiliki dua antarmuka dengan nama anggota dan tanda tangan yang sama, tetapi ingin mengubah perilakunya tergantung bagaimana penggunaannya. (Saya tidak merekomendasikan menulis kode seperti ini):
interface Cat
{
string Name {get;}
}
interface Dog
{
string Name{get;}
}
public class Animal : Cat, Dog
{
string Cat.Name
{
get
{
return "Cat";
}
}
string Dog.Name
{
get
{
return "Dog";
}
}
}
static void Main(string[] args)
{
Animal animal = new Animal();
Cat cat = animal; //Note the use of the same instance of Animal. All we are doing is picking which interface implementation we want to use.
Dog dog = animal;
Console.WriteLine(cat.Name); //Prints Cat
Console.WriteLine(dog.Name); //Prints Dog
}
public class Animal : Cat, Dog
Hal ini dapat menjaga kebersihan antarmuka publik untuk mengimplementasikan antarmuka secara eksplisit, misalnya File
kelas Anda mungkin mengimplementasikan IDisposable
secara eksplisit dan menyediakan metode publik Close()
yang mungkin lebih masuk akal bagi konsumen daripada Dispose(
).
F # saja menawarkan implementasi antarmuka eksplisit sehingga Anda selalu harus mentransmisikan ke antarmuka tertentu untuk mengakses fungsinya, yang membuat penggunaan antarmuka menjadi sangat eksplisit (tanpa maksud kata-kata).
Dispose
adalah hal-hal yang tidak akan pernah membutuhkan pembersihan); contoh yang lebih baik adalah implementasi dari koleksi yang tidak dapat diubah IList<T>.Add
.
Jika Anda memiliki antarmuka internal dan Anda tidak ingin menerapkan anggota di kelas Anda secara publik, Anda akan menerapkannya secara eksplisit. Implementasi implisit harus diketahui publik.
Alasan lain untuk implementasi eksplisit adalah untuk pemeliharaan .
Ketika kelas menjadi "sibuk" - ya itu terjadi, kita tidak semua memiliki kemewahan untuk memfaktorkan ulang kode anggota tim lain - kemudian memiliki implementasi eksplisit memperjelas bahwa ada metode di sana untuk memenuhi kontrak antarmuka.
Jadi itu meningkatkan "keterbacaan" kode.
#region
, dengan string judul yang sesuai. Dan komentar tentang metodenya.
Contoh berbeda diberikan oleh System.Collections.Immutable
, di mana penulis memilih untuk menggunakan teknik untuk mempertahankan API yang sudah dikenal untuk jenis koleksi sambil membuang bagian antarmuka yang tidak memiliki arti untuk jenis baru mereka.
Secara konkret, ImmutableList<T>
mengimplementasikan IList<T>
dan dengan demikian ICollection<T>
( agar memungkinkan ImmutableList<T>
untuk digunakan lebih mudah dengan kode warisan), namun void ICollection<T>.Add(T item)
tidak masuk akal untuk ImmutableList<T>
: karena menambahkan elemen ke daftar yang tidak dapat diubah tidak boleh mengubah daftar yang ada, ImmutableList<T>
juga berasal dari IImmutableList<T>
yang IImmutableList<T> Add(T item)
dapat digunakan untuk daftar yang tidak dapat diubah.
Jadi dalam kasus Add
, implementasi pada ImmutableList<T>
akhirnya terlihat sebagai berikut:
public ImmutableList<T> Add(T item)
{
// Create a new list with the added item
}
IImmutableList<T> IImmutableList<T>.Add(T value) => this.Add(value);
void ICollection<T>.Add(T item) => throw new NotSupportedException();
int IList.Add(object value) => throw new NotSupportedException();
Dalam kasus antarmuka yang ditentukan secara eksplisit, semua metode secara otomatis menjadi pribadi, Anda tidak dapat memberikan pengubah akses publik kepada mereka. Seharusnya:
interface Iphone{
void Money();
}
interface Ipen{
void Price();
}
class Demo : Iphone, Ipen{
void Iphone.Money(){ //it is private you can't give public
Console.WriteLine("You have no money");
}
void Ipen.Price(){ //it is private you can't give public
Console.WriteLine("You have to paid 3$");
}
}
// So you have to cast to call the method
class Program
{
static void Main(string[] args)
{
Demo d = new Demo();
Iphone i1 = (Iphone)d;
i1.Money();
((Ipen)i1).Price();
Console.ReadKey();
}
}
// You can't call methods by direct class object
Beginilah cara kita membuat Antarmuka Eksplisit: Jika kita memiliki 2 antarmuka dan kedua antarmuka memiliki metode yang sama dan satu kelas mewarisi 2 antarmuka ini, sehingga ketika kita memanggil satu metode antarmuka, kompilator bingung metode mana yang akan dipanggil, jadi kita bisa mengelola masalah ini menggunakan Antarmuka Eksplisit. Berikut adalah salah satu contoh yang saya berikan di bawah ini.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace oops3
{
interface I5
{
void getdata();
}
interface I6
{
void getdata();
}
class MyClass:I5,I6
{
void I5.getdata()
{
Console.WriteLine("I5 getdata called");
}
void I6.getdata()
{
Console.WriteLine("I6 getdata called");
}
static void Main(string[] args)
{
MyClass obj = new MyClass();
((I5)obj).getdata();
Console.ReadLine();
}
}
}