Bisakah Anda jelaskan apa penggunaan praktis untuk internal
kata kunci dalam C #?
Saya tahu bahwa internal
pengubah membatasi akses ke perakitan saat ini, tetapi kapan dan dalam keadaan apa saya harus menggunakannya?
Bisakah Anda jelaskan apa penggunaan praktis untuk internal
kata kunci dalam C #?
Saya tahu bahwa internal
pengubah membatasi akses ke perakitan saat ini, tetapi kapan dan dalam keadaan apa saya harus menggunakannya?
Jawaban:
Kelas utilitas / helper / metode yang ingin Anda akses dari banyak kelas lain dalam majelis yang sama, tetapi Anda ingin memastikan kode di majelis lain tidak dapat diakses.
Dari MSDN (via archive.org):
Penggunaan umum dari akses internal dalam pengembangan berbasis komponen karena memungkinkan sekelompok komponen untuk bekerja sama secara pribadi tanpa terkena sisa kode aplikasi. Misalnya, kerangka kerja untuk membangun antarmuka pengguna grafis dapat memberikan kelas Kontrol dan Formulir yang bekerja sama menggunakan anggota dengan akses internal. Karena anggota ini bersifat internal, mereka tidak terkena kode yang menggunakan kerangka kerja.
Anda juga dapat menggunakan pengubah internal bersama dengan InternalsVisibleTo
atribut level rakitan untuk membuat rakitan "teman" yang diberikan akses khusus ke kelas internal rakitan target.
Ini dapat berguna untuk membuat rakitan pengujian unit yang kemudian diizinkan memanggil anggota internal rakitan untuk diuji. Tentu saja tidak ada majelis lain yang diberikan tingkat akses ini, jadi ketika Anda merilis sistem Anda, enkapsulasi dipertahankan.
Jika Bob membutuhkan BigImportantClass maka Bob perlu membuat orang yang memiliki proyek A untuk mendaftar untuk menjamin bahwa BigImportantClass akan ditulis untuk memenuhi kebutuhannya, diuji untuk memastikan bahwa itu memenuhi kebutuhannya, didokumentasikan sebagai memenuhi kebutuhannya, dan bahwa proses akan diberlakukan untuk memastikan bahwa itu tidak akan pernah diubah sehingga tidak lagi memenuhi kebutuhannya.
Jika suatu kelas bersifat internal maka ia tidak harus melalui proses itu, yang menghemat anggaran untuk Proyek A yang dapat mereka belanjakan untuk hal-hal lain.
Inti dari internal bukanlah bahwa itu membuat hidup Bob sulit. Ini memungkinkan Anda untuk mengontrol apa yang dijanjikan oleh Proyek A tentang fitur, masa pakai, kompatibilitas, dan sebagainya.
Alasan lain untuk menggunakan internal adalah jika Anda mengaburkan binari Anda. Obfuscator tahu bahwa aman untuk mengacak nama kelas dari setiap kelas internal, sedangkan nama kelas publik tidak dapat diacak, karena itu dapat merusak referensi yang ada.
Jika Anda menulis DLL yang merangkum satu ton fungsi kompleks ke dalam API publik sederhana, maka "internal" digunakan pada anggota kelas yang tidak akan diekspos secara publik.
Menyembunyikan kompleksitas (alias enkapsulasi) adalah konsep utama rekayasa perangkat lunak berkualitas.
Kata kunci internal banyak digunakan saat Anda membuat wrapper di atas kode yang tidak dikelola.
Ketika Anda memiliki pustaka berbasis C / C ++ yang ingin Anda DllImport, Anda dapat mengimpor fungsi-fungsi ini sebagai fungsi statis kelas, dan menjadikannya internal, sehingga pengguna Anda hanya memiliki akses ke pembungkus Anda dan bukan API asli sehingga tidak bisa mengacaukan apapun. Fungsi-fungsi menjadi statis, Anda dapat menggunakannya di mana saja dalam perakitan, untuk beberapa kelas pembungkus yang Anda butuhkan.
Anda dapat melihat Mono.Cairo, ini adalah pembungkus di sekitar perpustakaan cairo yang menggunakan pendekatan ini.
Didorong oleh "gunakan pengubah ketat seperti yang Anda bisa" aturan saya menggunakan internal di mana-mana saya perlu mengakses, katakanlah, metode dari kelas lain sampai saya secara eksplisit perlu mengaksesnya dari majelis lain.
Karena antarmuka perakitan biasanya lebih sempit daripada jumlah antarmuka kelasnya, ada cukup banyak tempat yang saya gunakan.
Saya menemukan internal terlalu banyak digunakan. Anda benar-benar tidak boleh mengekspos fungsi tertentu hanya untuk kelas-kelas tertentu yang Anda tidak akan ke konsumen lain.
Ini menurut saya merusak antarmuka, merusak abstraksi. Ini bukan untuk mengatakan itu tidak boleh digunakan, tetapi solusi yang lebih baik adalah dengan refactor ke kelas yang berbeda atau untuk digunakan dengan cara yang berbeda jika memungkinkan. Namun, ini mungkin tidak selalu memungkinkan.
Alasan yang dapat menyebabkan masalah adalah bahwa pengembang lain mungkin dituduh membangun kelas lain di majelis yang sama dengan milik Anda. Memiliki internal mengurangi kejelasan abstraksi, dan dapat menyebabkan masalah jika disalahgunakan. Ini akan menjadi masalah yang sama seolah-olah Anda membuatnya publik. Kelas lain yang sedang dibangun oleh pengembang lain masih merupakan konsumen, sama seperti kelas eksternal lainnya. Abstraksi dan enkapsulasi kelas bukan hanya untuk perlindungan bagi / dari kelas eksternal, tetapi untuk semua dan semua kelas.
Masalah lain adalah bahwa banyak pengembang akan berpikir mereka mungkin perlu menggunakannya di tempat lain dalam perakitan dan menandainya sebagai internal, meskipun mereka tidak memerlukannya saat itu. Pengembang lain mungkin berpikir itu ada untuk diambil. Biasanya Anda ingin menandai pribadi sampai Anda memiliki kebutuhan pasti.
Tetapi beberapa di antaranya bisa subjektif, dan saya tidak mengatakan itu tidak boleh digunakan. Cukup gunakan saat dibutuhkan.
Melihat yang menarik beberapa hari yang lalu, mungkin minggu, di sebuah blog yang saya tidak ingat. Pada dasarnya saya tidak dapat mengambil kredit untuk ini tetapi saya pikir itu mungkin memiliki beberapa aplikasi yang bermanfaat.
Katakanlah Anda ingin kelas abstrak dilihat oleh majelis lain tetapi Anda tidak ingin seseorang dapat mewarisinya. Sealed tidak akan berfungsi karena abstrak karena suatu alasan, kelas-kelas lain dalam majelis itu memang mewarisinya. Pribadi tidak akan berfungsi karena Anda mungkin ingin mendeklarasikan kelas Induk di suatu tempat di majelis lain.
namespace Base. Perakitan { Parent kelas publik abstrak { void abstrak internal SomeMethod (); } // Ini bekerja dengan baik karena berada di majelis yang sama. kelas publik ChildWithin: Parent { internal override batal SomeMethod () { } } } namespace Another. Perakitan { // Kaboom, karena Anda tidak dapat mengganti metode internal kelas publik ChildOutside: Induk { } Tes kelas publik { //Baik baik saja Parent pribadi _parent; Tes publik () { //Masih baik _parent = ChildWithin baru (); } } }
Seperti yang Anda lihat, ini secara efektif memungkinkan seseorang untuk menggunakan kelas Induk tanpa dapat mewarisinya.
Contoh ini berisi dua file: Assembly1.cs dan Assembly2.cs. File pertama berisi kelas basis internal, BaseClass. Di file kedua, upaya untuk instantiate BaseClass akan menghasilkan kesalahan.
// Assembly1.cs
// compile with: /target:library
internal class BaseClass
{
public static int intM = 0;
}
// Assembly1_a.cs
// compile with: /reference:Assembly1.dll
class TestAccess
{
static void Main()
{
BaseClass myBase = new BaseClass(); // CS0122
}
}
Dalam contoh ini, gunakan file yang sama yang Anda gunakan dalam contoh 1, dan ubah tingkat aksesibilitas BaseClass ke publik . Juga ubah tingkat aksesibilitas anggota IntM ke internal . Dalam hal ini, Anda dapat membuat instance kelas, tetapi Anda tidak dapat mengakses anggota internal.
// Assembly2.cs
// compile with: /target:library
public class BaseClass
{
internal static int intM = 0;
}
// Assembly2_a.cs
// compile with: /reference:Assembly1.dll
public class TestAccess
{
static void Main()
{
BaseClass myBase = new BaseClass(); // Ok.
BaseClass.intM = 444; // CS0117
}
}
sumber : http://msdn.microsoft.com/en-us/library/7c5ka91b(VS.80).aspx
Ketika Anda memiliki metode, kelas, dll yang perlu diakses dalam lingkup perakitan saat ini dan tidak pernah di luar itu.
Misalnya, DAL mungkin memiliki ORM tetapi objek tidak boleh terkena lapisan bisnis semua interaksi harus dilakukan melalui metode statis dan melewati parameter yang diperlukan.
Penggunaan internal yang sangat menarik - dengan anggota internal tentu saja hanya terbatas pada majelis yang dideklarasikan - membuat fungsi "teman" sedikit banyak darinya. Anggota teman adalah sesuatu yang hanya dapat dilihat oleh majelis tertentu di luar majelis yang dideklarasikan. C # tidak memiliki dukungan bawaan untuk teman, namun CLR melakukannya.
Anda dapat menggunakan InternalsVisibleToAttribute untuk mendeklarasikan rakitan teman, dan semua referensi dari dalam rakitan teman akan memperlakukan anggota internal rakitan pernyataan Anda sebagai publik dalam lingkup rakitan teman. Masalah dengan ini adalah bahwa semua anggota internal terlihat; Anda tidak dapat memilih.
Penggunaan yang baik untuk InternalsVisibleTo adalah untuk mengekspos berbagai anggota internal ke unit uji unit sehingga menghilangkan kebutuhan untuk pekerjaan refleksi kompleks di sekitar untuk menguji anggota tersebut. Semua anggota internal yang terlihat tidak begitu menjadi masalah, namun mengambil pendekatan ini cukup memberatkan antarmuka kelas Anda dan berpotensi merusak enkapsulasi di dalam deklarasi majelis.
Sebagai aturan umum ada dua jenis anggota:
Kelas internal memungkinkan Anda untuk membatasi API perakitan Anda. Ini memiliki manfaat, seperti membuat API Anda lebih mudah dipahami.
Juga, jika ada bug di rakitan Anda, ada sedikit kemungkinan perbaikan memperbaiki perubahan. Tanpa kelas internal, Anda harus mengasumsikan bahwa mengubah anggota publik kelas mana pun akan menjadi perubahan besar. Dengan kelas internal, Anda dapat mengasumsikan bahwa memodifikasi anggota publik mereka hanya merusak API internal majelis (dan majelis yang dirujuk dalam atribut InternalsVisibleTo).
Saya suka memiliki enkapsulasi di tingkat kelas dan di tingkat perakitan. Ada beberapa yang tidak setuju dengan ini, tetapi senang mengetahui bahwa fungsi ini tersedia.
Saya punya proyek yang menggunakan LINQ-to-SQL untuk back-end data. Saya memiliki dua ruang nama utama: Biz dan Data. Model data LINQ hidup dalam Data dan ditandai "internal"; namespace Biz memiliki kelas publik yang membungkus kelas data LINQ.
Jadi disana Data.Client
, dan Biz.Client
; yang terakhir memperlihatkan semua properti yang relevan dari objek data, misalnya:
private Data.Client _client;
public int Id { get { return _client.Id; } set { _client.Id = value; } }
Objek Biz memiliki konstruktor pribadi (untuk memaksa penggunaan metode pabrik), dan konstruktor internal yang terlihat seperti ini:
internal Client(Data.Client client) {
this._client = client;
}
Itu dapat digunakan oleh salah satu kelas bisnis di perpustakaan, tetapi front-end (UI) tidak memiliki cara langsung mengakses model data, memastikan bahwa lapisan bisnis selalu bertindak sebagai perantara.
Ini adalah pertama kalinya saya benar-benar menggunakan internal
banyak, dan ini terbukti sangat berguna.
Ada kasus ketika masuk akal untuk membuat anggota kelas internal
. Salah satu contoh bisa jadi jika Anda ingin mengontrol bagaimana kelas-kelas tersebut dipakai; katakanlah Anda menyediakan semacam pabrik untuk membuat instance kelas. Anda dapat membuat konstruktor internal
, sehingga pabrik (yang berada di rakitan yang sama) dapat membuat instance kelas, tetapi kode di luar rakitan itu tidak bisa.
Namun, saya tidak dapat melihat poin apa pun dengan membuat kelas atau anggota internal
tanpa alasan khusus, sesedikit yang masuk akal untuk membuatnya public
, atau private
tanpa alasan spesifik.
satu-satunya hal yang pernah saya gunakan pada kata kunci internal adalah kode pemeriksaan lisensi di produk saya ;-)
Salah satu penggunaan kata kunci internal adalah untuk membatasi akses ke implementasi konkret dari pengguna majelis Anda.
Jika Anda memiliki pabrik atau lokasi pusat lainnya untuk membuat objek, pengguna perakitan Anda hanya perlu berurusan dengan antarmuka publik atau kelas dasar abstrak.
Selain itu, konstruktor internal memungkinkan Anda untuk mengontrol di mana dan kapan kelas publik dinyatakan dipakai.
Bagaimana dengan yang ini: biasanya disarankan agar Anda tidak mengekspos objek Daftar ke pengguna eksternal dari sebuah rakitan, melainkan mengekspos sebuah IEnumerable. Tetapi jauh lebih mudah untuk menggunakan objek Daftar di dalam rakitan, karena Anda mendapatkan sintaks array, dan semua metode Daftar lainnya. Jadi, saya biasanya memiliki properti internal yang memperlihatkan Daftar untuk digunakan di dalam perakitan.
Komentar dipersilahkan tentang pendekatan ini.
Perlu diingat bahwa setiap kelas yang didefinisikan sebagai public
akan secara otomatis muncul di intellisense ketika seseorang melihat namespace proyek Anda. Dari perspektif API, penting untuk hanya menunjukkan kepada pengguna proyek Anda kelas-kelas yang dapat mereka gunakan. Gunakan internal
kata kunci untuk menyembunyikan hal-hal yang seharusnya tidak mereka lihat.
Jika Big_Important_Class
untuk Proyek A Anda dimaksudkan untuk digunakan di luar proyek Anda, maka Anda tidak boleh menandainya internal
.
Namun, di banyak proyek, Anda akan sering memiliki kelas yang benar-benar hanya dimaksudkan untuk digunakan di dalam proyek. Misalnya, Anda mungkin memiliki kelas yang menyimpan argumen untuk permintaan utas parameterized. Dalam kasus ini, Anda harus menandai mereka seolah- internal
olah tanpa alasan lain selain untuk melindungi diri Anda dari perubahan API yang tidak disengaja.
private
kunci hanya dapat digunakan pada kelas atau struct yang didefinisikan dalam kelas atau struct lain. Kelas yang didefinisikan dalam namespace hanya dapat dideklarasikan public
atau internal
.
Idenya adalah bahwa ketika Anda mendesain perpustakaan hanya kelas-kelas yang dimaksudkan untuk digunakan dari luar (oleh klien perpustakaan Anda) harus publik. Dengan cara ini Anda bisa menyembunyikan kelas itu
dll.
Jika Anda mengembangkan solusi internal daripada menggunakan elemen internal tidak begitu penting, saya kira, karena biasanya klien akan selalu berhubungan dengan Anda dan / atau akses ke kode. Mereka cukup penting untuk pengembang perpustakaan.
Ketika Anda memiliki kelas atau metode yang tidak sesuai dengan Paradigma Berorientasi Objek, yang melakukan hal-hal berbahaya, yang perlu dipanggil dari kelas dan metode lain di bawah kendali Anda, dan yang Anda tidak ingin membiarkan orang lain menggunakan .
public class DangerousClass {
public void SafeMethod() { }
internal void UpdateGlobalStateInSomeBizarreWay() { }
}