Mengapa antarmuka C # tidak bisa berisi bidang?


223

Sebagai contoh, misalkan saya ingin ICarantarmuka dan bahwa semua implementasi akan berisi bidang Year. Apakah ini berarti bahwa setiap implementasi harus secara terpisah menyatakan Year? Bukankah lebih baik mendefinisikannya di antarmuka saja?


21
Antarmuka tidak memiliki implementasi, karena ini menggunakan kelas abstrak, dengan properti Year
PostMan

6
Untuk menambah apa yang telah dikatakan di sini, antarmuka adalah kontrak, dan bidang adalah detail implementasi yang mendefinisikan slot dalam memori mesin untuk memasukkan nilai (skalar atau pointer alamat) ke dalamnya.
herzmeister

5
Tetapi jika lapangan bersifat publik, itu adalah bagian dari kontrak dan bukan hanya detail implementasi, bukan?
Alex

Jawaban:


238

Meskipun banyak jawaban lain yang benar di tingkat semantik, saya merasa menarik untuk juga mendekati pertanyaan-pertanyaan semacam ini dari tingkat rincian implementasi.

Antarmuka dapat dianggap sebagai kumpulan slot , yang berisi metode . Ketika sebuah kelas mengimplementasikan sebuah antarmuka, kelas tersebut diperlukan untuk memberi tahu runtime bagaimana mengisi semua slot yang diperlukan. Kapan kamu berkata

interface IFoo { void M(); } 
class Foo : IFoo { public void M() { ... } }

kelas mengatakan "ketika Anda membuat instance dari saya, isikan referensi untuk Foo.M di slot untuk IFoo.M.

Kemudian saat Anda melakukan panggilan:

IFoo ifoo = new Foo();
ifoo.M();

kompiler menghasilkan kode yang mengatakan "tanyakan objek metode apa yang ada di slot untuk IFoo.M, dan panggil metode itu.

Jika sebuah antarmuka adalah kumpulan slot yang berisi metode, maka beberapa slot itu juga bisa berisi metode get and set dari properti, metode get and set dari pengindeks, dan metode tambah dan hapus acara. Tapi lapangan bukan metode . Tidak ada "slot" yang terkait dengan bidang yang dapat Anda "isi" dengan referensi ke lokasi bidang. Dan karena itu, antarmuka dapat mendefinisikan metode, properti, pengindeks dan peristiwa, tetapi tidak bidang.


26
Satu hal yang kadang-kadang saya lewatkan adalah kemampuan mirip java untuk mendefinisikan konstanta tingkat antarmuka, yang mungkin tidak memerlukan "slot" untuk mendukung dalam bahasa tersebut.
LBushkin

2
Saya suka penjelasannya dengan kata-kata sederhana. Terima kasih. "CLR via C #" dan "Essential .net volume 1" memberikan detail lebih lanjut.
Sandeep GB

6
Mengapa suatu bidang tidak memiliki slot? dan pertanyaan yang sama dengan operator? Saya ingat pernah mendengar tentang mengetik bebek menggunakan refleksi untuk melihat apakah antarmuka diimplementasikan bahkan jika kelas tidak mewarisi antarmuka. Mengapa cant refleksi (atau slot) digunakan untuk menarik bidang? Saya masih menulis kode saya jadi saya mungkin tidak perlu / ingin bidang tetapi saya terkejut menemukan saya tidak dapat menggunakan operator. operator yang persis seperti metode dari pemahaman saya kecuali tidak semua dapat kelebihan beban ( An interface cannot contain constants, fields, operatorsDari. msdn.microsoft.com/en-us/library/ms173156.aspx )

@acidzombie: Pertanyaan mengapa antarmuka tidak dapat mendefinisikan operator berbeda (meskipun mungkin terkait) dengan mengapa tidak dapat berisi bidang; Saya sarankan memposting pertanyaan lain jika Anda masih tertarik dengan itu.
Adam Robinson

1
@ b1nary.atr0phy mengapa dengan bidang? Misal jika saya mendeklarasikan metode int One(), maka implementasinya public int One(){return 1;}bukan bidang.
Hi-Angel

131

Antarmuka dalam C # dimaksudkan untuk mendefinisikan kontrak yang akan dipatuhi suatu kelas - bukan implementasi tertentu.

Dalam semangat itu, antarmuka C # memang memungkinkan properti untuk didefinisikan - yang pemanggil harus menyediakan implementasi untuk:

interface ICar
{
    int Year { get; set; }
}

Kelas implementasi dapat menggunakan properti otomatis untuk menyederhanakan implementasi, jika tidak ada logika khusus yang terkait dengan properti:

class Automobile : ICar
{
    public int Year { get; set; } // automatically implemented
}

5
Bukankah semua yang publik merupakan bagian dari kontrak. Jika suatu kelas memiliki Tahun int publik, bukankah dikatakan bahwa kontrak kelas memiliki bidang bertipe Tahun untuk hadir di sana, dan dapat diakses?
Didier A.

1
Terlambat ke pesta, tetapi tidak, dalam hal ini berarti kontrak memiliki Tahun PROPERTI yang seharusnya diterapkan oleh setiap kelas yang berlaku. Properti sebenarnya adalah metode get / set, yang memiliki bidang dukungan otomatis dihasilkan jika tidak ada logika khusus yang diperlukan. Sintaks khusus hanya untuk notasi yang lebih jelas.
user3613916

Bagaimana saya bisa mendefinisikan nilai default konstan (seperti 123) untuk implementasi otomatis Year?
lama12345

1
@ lama12345 Saya juga terlambat ke pesta, tetapi karena C # 6 (2015, .NET Framework 4.6 dan .NET Core) Anda dapat menggunakan properti-otomatis untuk tujuan itu. public int Year => 123;. Namun, dalam hal ini tidak masuk akal untuk memiliki setter, sehingga antarmuka harus didefinisikan denganint Year { get; }
EriF89

56

Nyatakan sebagai properti:

interface ICar {
   int Year { get; set; }
}

47
Pertanyaannya adalah " Mengapa antarmuka C # tidak bisa berisi bidang?". Ini tidak membahas itu.
AakashM

14
Saya setuju bahwa jawaban ini tidak menjawab pertanyaan OP, tapi yah, itu memecahkan masalah saya.
Gorgen

36

Eric Lippert memakukannya, saya akan menggunakan cara yang berbeda untuk mengatakan apa yang dia katakan. Semua anggota antarmuka adalah virtual dan mereka semua harus ditimpa oleh kelas yang mewarisi antarmuka. Anda tidak secara eksplisit menulis kata kunci virtual dalam deklarasi antarmuka, atau menggunakan kata kunci override di kelas, mereka tersirat.

Kata kunci virtual diimplementasikan dalam .NET dengan metode dan apa yang disebut v-table, sebuah array dari pointer metode. Kata kunci override mengisi slot v-table dengan pointer metode yang berbeda, menimpa yang diproduksi oleh kelas dasar. Properti, acara, dan pengindeks diimplementasikan sebagai metode di bawah tenda. Tapi ladang tidak. Antarmuka karena itu tidak dapat berisi bidang.


Anda memberikan nama teknis / aktual (v-table) untuk konsep slot yang disebutkan oleh Eric. Terima kasih untuk detailnya Hans.
RBT

Apa gunanya tabel-v jika antarmuka tidak diizinkan implementasi standar? Ini berubah di C # 8.0, tapi itu intinya.
AnthonyMonterrosa

19

Mengapa tidak hanya memiliki Yearproperti, yang tidak apa-apa?

Antarmuka tidak berisi bidang karena bidang mewakili implementasi representasi data yang spesifik, dan mengeksposnya akan memecah enkapsulasi. Dengan demikian memiliki antarmuka dengan bidang secara efektif akan menjadi pengkodean untuk implementasi, bukan antarmuka, yang merupakan paradoks penasaran untuk memiliki antarmuka!

Misalnya, bagian dari Yearspesifikasi Anda mungkin mengharuskan ICarpelaksana tidak valid untuk mengizinkan penugasan Yearyang lebih lambat dari tahun berjalan +1 atau sebelum 1900. Tidak ada cara untuk mengatakan bahwa jika Anda telah membuka Yearbidang - jauh lebih baik menggunakan properti bukan untuk melakukan pekerjaan di sini.


18

Jawaban singkatnya adalah ya, setiap jenis implementasi harus membuat variabel pendukungnya sendiri. Ini karena antarmuka analog dengan kontrak. Yang dapat dilakukan hanyalah menentukan bagian kode yang dapat diakses publik yang harus tersedia oleh suatu tipe implementasi; itu tidak dapat berisi kode apa pun itu sendiri.

Pertimbangkan skenario ini menggunakan apa yang Anda sarankan:

public interface InterfaceOne
{
    int myBackingVariable;

    int MyProperty { get { return myBackingVariable; } }
}

public interface InterfaceTwo
{
    int myBackingVariable;

    int MyProperty { get { return myBackingVariable; } }
}

public class MyClass : InterfaceOne, InterfaceTwo { }

Kami memiliki beberapa masalah di sini:

  • Karena semua anggota antarmuka - menurut definisi - publik, variabel pendukung kami sekarang terpapar kepada siapa saja yang menggunakan antarmuka
  • Yang myBackingVariableakan MyClassdigunakan?

Pendekatan yang paling umum diambil adalah dengan mendeklarasikan antarmuka dan kelas abstrak barebones yang mengimplementasikannya. Ini memungkinkan Anda fleksibilitas untuk mewarisi dari kelas abstrak dan mendapatkan implementasi secara gratis, atau secara eksplisit mengimplementasikan antarmuka dan diizinkan untuk mewarisi dari kelas lain. Ini berfungsi seperti ini:

public interface IMyInterface
{
    int MyProperty { get; set; }
}

public abstract class MyInterfaceBase : IMyInterface
{
    int myProperty;

    public int MyProperty
    {
        get { return myProperty; }
        set { myProperty = value; }
    }
}

7

Orang lain telah memberikan 'Mengapa', jadi saya hanya akan menambahkan bahwa antarmuka Anda dapat mendefinisikan Kontrol; jika Anda membungkusnya di properti:

public interface IView {
    Control Year { get; }
}


public Form : IView {
    public Control Year { get { return uxYear; } } //numeric text box or whatever
}

3

Antarmuka tidak mengandung implementasi apa pun.

  1. Tentukan antarmuka dengan properti.
  2. Selanjutnya Anda dapat mengimplementasikan antarmuka itu di kelas apa pun dan menggunakan kelas ini untuk maju.
  3. Jika diperlukan Anda dapat memiliki properti ini didefinisikan sebagai virtual di kelas sehingga Anda dapat mengubah perilakunya.

3

Banyak yang sudah dikatakan, tetapi untuk membuatnya sederhana, ini pendapat saya. Antarmuka dimaksudkan untuk memiliki kontrak metode yang akan dilaksanakan oleh konsumen atau kelas dan tidak memiliki bidang untuk menyimpan nilai.

Anda mungkin berpendapat bahwa mengapa properti diperbolehkan? Jadi jawaban sederhananya adalah - properti didefinisikan secara internal sebagai metode saja.


1
Jika Anda perlu mengakses anggota, buat saja properti dan Anda akan menjadi baik.
Unome

0

Untuk ini, Anda dapat memiliki kelas dasar Mobil yang mengimplementasikan bidang tahun, dan semua implementasi lainnya dapat mewarisinya.


0

Antarmuka mendefinisikan properti instance publik dan metode. Bidang biasanya bersifat pribadi, atau paling banyak dilindungi, internal, atau internal terlindungi (istilah "bidang" biasanya tidak digunakan untuk apa pun yang publik).

Seperti yang dinyatakan oleh balasan lain Anda dapat mendefinisikan kelas dasar dan mendefinisikan properti yang dilindungi yang akan dapat diakses oleh semua pewaris.

Satu keanehan adalah bahwa suatu antarmuka sebenarnya dapat didefinisikan sebagai internal tetapi membatasi kegunaan antarmuka, dan biasanya digunakan untuk mendefinisikan fungsionalitas internal yang tidak digunakan oleh kode eksternal lainnya.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.