Penggunaan praktis untuk kata kunci “internal” di C #

420

Bisakah Anda jelaskan apa penggunaan praktis untuk internalkata kunci dalam C #?

Saya tahu bahwa internalpengubah membatasi akses ke perakitan saat ini, tetapi kapan dan dalam keadaan apa saya harus menggunakannya?

Alexander Prokofyev
sumber

Jawaban:

379

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 InternalsVisibleToatribut 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.

Abu
sumber
8
Atribut InternalsVisibleTo sangat membantu. Terima kasih.
erict
33
Perasaan saya adalah bahwa sejak Anda membutuhkan internal, ada sesuatu yang salah dengan desain di suatu tempat. IMHO, seseorang harus dapat menggunakan OO murni untuk menyelesaikan semua masalah. Pada saat ini, saya sedang menatap sekitar satu juta penggunaan internal dalam sumber .NET Framework, menghalangi cara saya untuk memanfaatkan hal-hal yang mereka gunakan sendiri. Melalui penggunaan internal, mereka menciptakan monolith yang modular dangkal, tetapi rusak ketika Anda mencoba untuk mengisolasi barang-barang. Juga, keamanan bukanlah argumen, karena ada refleksi untuk penyelamatan.
Meringis Putus Asa
26
@GrimaceofDespair: Biarkan saya menambahkan komentar yang tidak setuju di sini. Saya pribadi melihat internal sebagai versi yang sangat berguna dari pengubah "publik" dalam solusi multi-proyek besar. Menambahkan pengubah ini ke kelas di sub-proyek saya melarang "penggunaan acak" kelas ini oleh sub-proyek lain dalam solusi dan memungkinkan saya untuk memaksakan penggunaan perpustakaan saya dengan tepat. Jika saya perlu melakukan beberapa refactoring nanti, saya tidak perlu bertanya pada diri sendiri "tunggu, tapi mengapa orang itu pergi dan membuat instance dari kelas Point yang hanya saya butuhkan untuk keperluan saya sendiri di dalam perhitungan khusus ini"?
KT.
3
Dengan kata lain, jika semua orang bergantung pada internal SmtpClient, dan pada suatu saat bug ditemukan di sana, .NET akan memiliki masalah serius untuk memperbaiki bug itu, bahkan mungkin menyimpannya untuk waktu yang lama. Saya ingat pernah membaca artikel lucu yang menggambarkan panjangnya pengembang Windows untuk menjaga "kompatibilitas bugwards" dengan semua program lama yang memanfaatkan berbagai fitur internal dan bug. Mengulangi kesalahan ini dengan .NET tidak berarti.
KT.
13
Saya pikir internal WAY lebih bermanfaat daripada publik. Satu-satunya waktu Anda menggunakan publik adalah ketika Anda secara eksplisit mengatakan "Di sini, pengguna - saya menyediakan fungsi yang berguna untuk Anda gunakan." Jika Anda menulis aplikasi daripada perpustakaan pengguna, semuanya harus internal karena Anda tidak mencoba memberi orang fungsi apa pun untuk menelepon secara eksternal. Jika Anda menulis pustaka pengguna, maka hanya fungsi berguna yang Anda harapkan untuk diberikan kepada pengguna, memelihara, mendukung, dan tetap bertahan sampai akhir waktu harus menjadi publik. Kalau tidak, buat internal.
Darrell Plank
85

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.

Eric Lippert
sumber
5
Masuk akal. Semoga saja kita memiliki kemampuan untuk menimpa anggota internal, karena kita semua menyetujui orang dewasa di sini.
cwallenpoole
6
@ EricLippert Tidak cukup apa yang saya maksud. Jika saya mewarisi kode kompilasi yang memiliki "internal" di dalamnya, saya macet, bukan? Tidak ada cara untuk hanya memberitahu kompiler "Tidak, bahwa pengembang lain berbohong kepada Anda. Anda seharusnya mempercayai saya", kecuali saya menggunakan refleksi.
cwallenpoole
11
@cwallenpoole: Jika Anda menggunakan kode yang ditulis oleh pembohong yang menulis kode yang tidak Anda sukai, maka hentikan penggunaan kode mereka dan tulis kode Anda sendiri yang Anda sukai.
Eric Lippert
2
@ EricLippert Itu seharusnya menjadi lidah di pipi, saya tidak bermaksud menyinggung. (Saya kebanyakan hanya mencoba untuk mengkonfirmasi bahwa saya SOL dalam kasus seperti itu).
cwallenpoole
5
@cwallenpoole: Saya tidak tersinggung sedikit pun. Saya menawarkan nasihat yang baik jika Anda menemukan diri Anda terjebak dalam situasi yang tidak menguntungkan itu.
Eric Lippert
60

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.

Joel Mueller
sumber
4
Pff. Melihat bytecode dengan disassembler masih akan membuatnya cukup jelas apa yang dilakukan kode. Obfuscator bukan pencegah ringan bagi siapa pun yang benar-benar mencoba masuk ke dalam. Tidak pernah mendengar ada peretas yang berhenti hanya karena dia tidak mendapatkan nama fungsi yang berguna.
Nyerguds
28
jadi kamu tidak mengunci pintu saat kamu tidur, karena kamu belum pernah mendengar tentang pencuri yang dihentikan oleh kunci?
Sergio
4
Itu sebabnya saya mengacak nama kelas dan variabel dalam kode sumber. Anda mungkin bisa mengetahui apa itu PumpStateComparator, tetapi bisakah Anda menebak apa itu LpWj4mWE? #jobsecurity (Saya tidak melakukan ini, dan tolong jangan benar-benar melakukan ini, orang-orang internet.)
Slothario
32

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.

Jeffrey L Whitledge
sumber
20

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.

Edwin Jarvis
sumber
12

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.

Ilya Komakhin
sumber
3
Jika Anda "menggunakan pengubah ketat seperti yang Anda bisa" mengapa tidak pribadi kemudian?
R. Martinho Fernandes
6
Karena privat terlalu ketat ketika properti / metode kelas harus diakses di luar kelas, dan kasus-kasus ketika mereka harus diakses dari asse lain, bly tidak begitu sering.
Ilya Komakhin
11

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.

mattlant
sumber
Saya sering membangun objek domain yang anggotanya harus publik untuk objek domain lain dalam majelis yang sama. Namun, anggota yang sama ini TIDAK boleh tersedia untuk kode klien yang berada di luar majelis. Untuk situasi seperti ini, 'internal' sangat tepat.
Rock Anthony Johnson
8

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.

Alat Programmin
sumber
7

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

Murad Mohd Zain
sumber
6

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.

Aaron Powell
sumber
6

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.

cfeduke
sumber
5

Sebagai aturan umum ada dua jenis anggota:

  • permukaan publik : terlihat dari rakitan eksternal (publik, dilindungi, dan dilindungi internal): pemanggil tidak dipercaya, sehingga validasi parameter, dokumentasi metode, dll. diperlukan.
  • permukaan pribadi : tidak terlihat dari rakitan eksternal (privat dan internal, atau kelas internal): pemanggil umumnya dipercaya, sehingga validasi parameter, dokumentasi metode, dll. dapat dihilangkan.
Michael Damatov
sumber
4

Pengurangan kebisingan, semakin sedikit tipe yang Anda tampilkan, semakin sederhana perpustakaan Anda. Tamper proofing / Security adalah hal lain (walaupun Reflection dapat menang melawannya).

Berdalih
sumber
4

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.

xofz
sumber
2

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 internalbanyak, dan ini terbukti sangat berguna.

Keith Williams
sumber
2

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 internaltanpa alasan khusus, sesedikit yang masuk akal untuk membuatnya public, atau privatetanpa alasan spesifik.

Fredrik Mörk
sumber
1

satu-satunya hal yang pernah saya gunakan pada kata kunci internal adalah kode pemeriksaan lisensi di produk saya ;-)

Steven A. Lowe
sumber
1

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.

Andrew Kennan
sumber
1

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.

Samik R
sumber
@Samik - dapatkah Anda menjelaskan lebih lanjut, "biasanya disarankan agar Anda tidak mengekspos objek Daftar ke pengguna eksternal dari sebuah rakitan, alih-alih mengekspos jumlah IEn." mengapa ienumerable diutamakan?
Howiecamp
1
@ Howiecamp: Sebagian besar karena IEnumerable memberikan akses read-only ke daftar, di mana seolah-olah Anda mengekspos daftar secara langsung, itu dapat dimodifikasi oleh klien (seperti menghapus elemen dll.), Yang mungkin tidak disengaja.
Samik R
Selain itu, mengekspos Daftar atau IList membuat kontrak yang Anda akan selalu mengizinkan (misalnya) akses acak cepat ke data. Jika suatu hari Anda memutuskan untuk mengubah metode Anda untuk menggunakan koleksi lain, atau tidak ada koleksi sama sekali (imbal hasil) Anda harus membuat Daftar hanya untuk mengembalikan nilai, atau memutus kontrak dengan mengubah jenis pengembalian metode. Menggunakan jenis pengembalian yang kurang spesifik memberi Anda fleksibilitas.
1

Perlu diingat bahwa setiap kelas yang didefinisikan sebagai publicakan 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 internalkata kunci untuk menyembunyikan hal-hal yang seharusnya tidak mereka lihat.

Jika Big_Important_Classuntuk 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- internalolah tanpa alasan lain selain untuk melindungi diri Anda dari perubahan API yang tidak disengaja.

Matt Davis
sumber
Jadi mengapa tidak menggunakan pribadi saja?
Tahanan NOL
1
Kata privatekunci hanya dapat digunakan pada kelas atau struct yang didefinisikan dalam kelas atau struct lain. Kelas yang didefinisikan dalam namespace hanya dapat dideklarasikan publicatau internal.
Matt Davis
1

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

  1. Kemungkinan akan berubah di rilis mendatang (jika itu publik Anda akan melanggar kode klien)
  2. Tidak berguna bagi klien dan dapat menyebabkan kebingungan
  3. Tidak aman (sehingga penggunaan yang tidak tepat dapat merusak perpustakaan Anda dengan sangat buruk)

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.

Grzenio
sumber
-7

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() { }
}
yfeldblum
sumber
Apa yang kamu coba katakan? Tidak terlalu jelas. Setidaknya tidak untuk saya.
pqsk
Setuju dengan @pqsk
Anynomous Khan