Bagikan melalui


Sistem jenis C#

C# adalah bahasa yang sangat ditik. Setiap variabel dan konstanta memiliki jenis, seperti halnya setiap ekspresi yang mengevaluasi ke nilai. C# terutama menggunakan sistem jenis normatif. Sistem jenis normatif menggunakan nama untuk mengidentifikasi setiap jenis. Di C#, struct, class, dan interface tipe data, termasuk tipe data record, semuanya diidentifikasi berdasarkan nama mereka. Setiap deklarasi metode menentukan nama, tipe, dan jenis (nilai, referensi, atau output) untuk setiap parameter dan untuk nilai balik. Pustaka kelas .NET mendefinisikan jenis numerik bawaan dan jenis kompleks yang mewakili berbagai konstruksi. Konstruksi ini mencakup sistem file, koneksi jaringan, koleksi dan array objek, dan tanggal. Program C# umum menggunakan jenis dari pustaka kelas dan jenis yang ditentukan pengguna yang memodelkan konsep yang khusus untuk domain masalah program.

C# juga mendukung jenis struktural, seperti tuple dan jenis anonim. Jenis struktural ditentukan oleh nama dan jenis setiap anggota, dan urutan anggota dalam ekspresi. Jenis struktural tidak memiliki nama yang unik.

Informasi yang disimpan dalam tipe dapat menyertakan item berikut:

  • Ruang penyimpanan yang diperlukan oleh variabel dari jenis tertentu.
  • Nilai maksimum dan minimum yang dapat diwakilinya.
  • Anggota (metode, bidang, peristiwa, dan sebagainya) yang dikandungnya.
  • Tipe dasar yang diturunkan darinya.
  • Antarmuka yang diimplementasikannya.
  • Operasi yang diizinkan.

Pengkompilasi menggunakan informasi jenis untuk memastikan semua operasi yang dilakukan dalam kode Anda aman. Misalnya, jika Anda mendeklarasikan variabel jenis int, pengkompilasi memungkinkan Anda untuk menggunakan variabel dalam operasi tambahan dan pengurangan. Jika Anda mencoba melakukan operasi yang sama pada variabel jenis bool, pengkompilasi menghasilkan kesalahan, seperti yang ditunjukkan dalam contoh berikut:

int a = 5;
int b = a + 2; //OK

bool test = true;

// Error. Operator '+' cannot be applied to operands of type 'int' and 'bool'.
int c = a + test;

Nota

Pengembang C dan C++, perhatikan bahwa dalam C#, bool tidak dapat dikonversi ke int.

Pengkompilasi menyematkan informasi jenis ke dalam file yang dapat dieksekusi sebagai metadata. Runtime bahasa umum (CLR) menggunakan metadata tersebut pada waktu proses untuk menjamin keamanan jenis lebih lanjut saat mengalokasikan dan mengklaim kembali memori.

Menentukan jenis dalam deklarasi variabel

Ketika Anda mendeklarasikan variabel atau konstanta dalam program, Anda harus menentukan jenisnya atau menggunakan var kata kunci untuk membiarkan pengkompilasi menyimpulkan jenisnya. Contoh berikut menunjukkan beberapa deklarasi variabel yang menggunakan jenis numerik bawaan dan jenis yang ditentukan pengguna yang kompleks:

// Declaration only:
float temperature;
string name;
MyClass myClass;

// Declaration with initializers (four examples):
char firstLetter = 'C';
var limit = 3;
int[] source = [0, 1, 2, 3, 4, 5];
var query = from item in source
            where item <= limit
            select item;

Anda menentukan jenis parameter metode dan mengembalikan nilai dalam deklarasi metode. Tanda tangan berikut menunjukkan metode yang memerlukan int sebagai argumen input dan mengembalikan string:

public string GetName(int ID)
{
    if (ID < names.Length)
        return names[ID];
    else
        return String.Empty;
}
private string[] names = ["Spencer", "Sally", "Doug"];

Setelah mendeklarasikan variabel, Anda tidak dapat mendeklarasikannya kembali dengan jenis baru, dan Anda tidak dapat menetapkan nilai yang tidak kompatibel dengan jenis yang dideklarasikan. Misalnya, Anda tidak dapat mendeklarasikan int dan kemudian menetapkannya nilai Boolean .true Namun, Anda dapat mengonversi nilai ke tipe lain, seperti saat menetapkannya ke variabel baru atau meneruskannya sebagai argumen metode. Pengkompilasi secara otomatis melakukan konversi jenis yang tidak menyebabkan kehilangan data. Konversi yang dapat menyebabkan kehilangan data memerlukan cast dalam kode sumber.

Untuk informasi selengkapnya, lihat Pemeran dan Konversi Jenis.

Tipe bawaan

C# menyediakan sekumpulan jenis bawaan standar. Jenis ini mewakili bilangan bulat, nilai titik pecahan, ekspresi Boolean, karakter teks, nilai desimal, dan jenis data lainnya. Bahasa ini juga mencakup jenis bawaan string dan object. Anda dapat menggunakan jenis ini dalam program C# apa pun. Untuk daftar lengkap jenis bawaan, lihat Jenis bawaan.

Jenis kustom

Buat jenis struktural dengan menggunakan tuple untuk menyimpan anggota data terkait. Jenis-jenis ini menyediakan struktur yang menampung beberapa anggota. Tuple memiliki perilaku terbatas. Mereka adalah wadah untuk nilai. Ini adalah jenis paling sederhana yang dapat Anda buat. Anda nantinya dapat memutuskan bahwa Anda memerlukan perilaku. Dalam hal ini, Anda dapat mengonversi tuple menjadi struct atau class.

Gunakan struct, class, interface, enum, dan record untuk membuat jenis kustom Anda sendiri. Pustaka kelas .NET itu sendiri adalah kumpulan jenis kustom yang dapat Anda gunakan dalam aplikasi Anda sendiri. Secara default, jenis yang paling sering digunakan di pustaka kelas tersedia dalam program C# apa pun. Anda membuat jenis lain tersedia dengan secara eksplisit menambahkan referensi paket ke paket yang menyediakannya. Setelah pengkompilasi memiliki referensi ke paket, Anda dapat mendeklarasikan variabel dan konstanta jenis yang dideklarasikan dalam rakitan paket tersebut dalam kode sumber.

Salah satu keputusan pertama yang Anda buat saat menentukan jenis adalah memutuskan konstruksi mana yang akan digunakan untuk jenis Anda. Daftar berikut membantu membuat keputusan awal tersebut. Beberapa pilihan tumpang tindih. Dalam kebanyakan skenario, lebih dari satu opsi adalah pilihan yang wajar.

  • Jika jenis data bukan bagian dari domain aplikasi Anda dan tidak menyertakan perilaku, gunakan jenis struktural.
  • Jika ukuran penyimpanan data kecil, tidak lebih dari 64 byte, pilih struct atau record struct.
  • Jika jenisnya tidak dapat diubah, atau Anda menginginkan mutasi nondestruktif, pilih struct atau record struct.
  • Jika jenis Anda harus memiliki nilai semantik untuk kesetaraan, pilih record class atau record struct.
  • Jika jenisnya terutama untuk menyimpan data, dengan perilaku minimal, pilih record class atau record struct.
  • Jika jenisnya adalah bagian dari hierarki warisan, pilih record class atau class.
  • Jika jenis menggunakan polimorfisme, pilih class.
  • Jika tujuan utama adalah perilaku, pilih class.

Anda juga dapat memilih elemen interface untuk memodelkan sebuah kontrak: perilaku yang dijelaskan oleh anggotanya yang dapat diimplementasikan oleh jenis yang tidak terkait. Antarmuka bersifat abstrak dan mendeklarasikan anggota yang harus diimplementasikan oleh semua class atau struct tipe yang mewarisi dari antarmuka itu.

Sistem jenis umum

Sistem jenis umum mendukung prinsip warisan. Jenis dapat berasal dari jenis lain, yang disebut jenis dasar. Jenis turunan mewarisi (dengan beberapa batasan) metode, properti, dan anggota lain dari jenis dasar. Jenis dasar pada gilirannya dapat berasal dari beberapa jenis lain, dalam hal ini jenis turunan mewarisi anggota dari kedua jenis dasar dalam hierarki warisannya.

Semua jenis, termasuk jenis numerik bawaan seperti System.Int32 (kata kunci C#: int), pada akhirnya berasal dari satu jenis dasar, yaitu System.Object (kata kunci C#: object). Hierarki jenis terpadu ini disebut Common Type System (CTS). Untuk informasi selengkapnya tentang pewarisan di C#, lihat Warisan.

Setiap jenis dalam CTS didefinisikan sebagai jenis nilai atau jenis referensi. Jenis ini mencakup semua jenis kustom di pustaka kelas .NET dan juga jenis yang ditentukan pengguna Anda sendiri:

  • Jenis yang Anda tentukan dengan menggunakan struct kata kunci atau record struct adalah jenis nilai. Semua jenis numerik bawaan adalah structs.
  • Jenis yang Anda tentukan dengan menggunakan class, , record classatau record kata kunci adalah jenis referensi.

Jenis referensi dan jenis nilai memiliki aturan waktu kompilasi yang berbeda dan perilaku run-time yang berbeda.

Nota

Jenis yang paling umum digunakan semuanya terorganisir dalam System namespace. Namun, namespace di mana jenis dimuat tidak memiliki hubungan dengan apakah itu jenis nilai atau jenis referensi.

Kelas dan struktur adalah dua konstruksi dasar dari sistem jenis umum di .NET. Setiap konstruksi pada dasarnya adalah struktur data yang merangkum sekumpulan data dan perilaku yang dimiliki bersama sebagai unit logis. Data dan perilaku merupakan anggota dari kelas, struct, atau catatan. Anggota menyertakan metode, properti, peristiwa, dan sebagainya, seperti yang tercantum nanti dalam artikel ini.

Deklarasi kelas, struktur, atau rekaman seperti cetak biru yang Anda gunakan untuk membuat instans atau objek pada waktu proses. Jika Anda menentukan kelas, struct, atau rekaman bernama Person, Person adalah nama jenisnya. Jika Anda mendeklarasikan dan menginisialisasi variabel p jenis Person, p dikatakan sebagai objek atau instans Person. Anda dapat membuat beberapa instans dengan jenis yang sama Person , dan setiap instans dapat memiliki nilai yang berbeda di properti dan bidangnya.

Kelas adalah tipe referensi. Saat Anda membuat objek jenis , variabel tempat Anda menetapkan objek hanya menyimpan referensi ke memori tersebut. Saat Anda menetapkan referensi objek ke variabel baru, variabel baru mengacu pada objek asli. Perubahan yang Anda buat melalui satu variabel tercermin dalam variabel lain karena keduanya merujuk ke data yang sama.

Struct adalah tipe data nilai. Saat Anda membuat struct, variabel tempat Anda menetapkan struct menyimpan data struct yang sesungguhnya. Saat Anda menetapkan struct ke variabel baru, struct akan disalin. Oleh karena itu, variabel baru dan variabel asli berisi dua salinan terpisah dari data yang sama. Perubahan yang Anda buat pada satu salinan tidak memengaruhi salinan lainnya.

Jenis rekaman dapat berupa jenis referensi (record class) atau jenis nilai (record struct). Jenis rekaman berisi metode yang mendukung kesetaraan nilai.

Secara umum, gunakan kelas untuk memodelkan perilaku yang lebih kompleks. Kelas biasanya menyimpan data yang Anda ubah setelah objek kelas dibuat. Struktur paling cocok untuk struktur data kecil. Struktur biasanya menyimpan data yang tidak Anda ubah setelah struct dibuat. Tipe rekaman adalah struktur data dengan anggota yang disintesis oleh penyusun tambahan. Rekaman biasanya menyimpan data yang tidak Anda ubah setelah objek dibuat.

Jenis nilai

Jenis nilai berasal dari System.ValueType, yang berasal dari System.Object. Jenis yang berasal dari System.ValueType memiliki perilaku khusus di CLR. Variabel jenis nilai secara langsung berisi nilainya. Memori untuk struct dialokasikan secara langsung di dalam konteks apapun variabel dideklarasikan. Anda dapat mendeklarasikan record struct tipe nilai dan menyertakan anggota yang disintesis untuk rekor.

Ada dua kategori jenis nilai: struct dan enum.

Jenis numerik bawaan adalah struktur, dan memiliki bidang dan metode yang dapat Anda akses:

// constant field on type byte.
byte b = byte.MaxValue;

Tetapi Anda mendeklarasikan dan menetapkan nilai kepada mereka seolah-olah mereka adalah jenis non-agregat sederhana:

byte num = 0xA;
int i = 5;
char c = 'Z';

Jenis nilai tertutup . Anda tidak dapat memperoleh jenis dari jenis nilai apa pun, seperti System.Int32. Anda tidak dapat menentukan struct untuk mewarisi dari kelas atau struct yang ditentukan pengguna karena struct hanya dapat mewarisi dari System.ValueType. Namun, struct dapat mengimplementasikan satu atau beberapa antarmuka. Anda dapat mengubah tipe struct ke tipe antarmuka apa pun yang diimplementasikannya. Pemeranan ini menyebabkan operasi boxing membungkus struktur di dalam objek jenis referensi pada heap yang dikelola. Operasi tinju terjadi ketika Anda meneruskan tipe nilai ke metode yang menerima System.Object atau tipe antarmuka apa pun sebagai parameter input. Untuk informasi selengkapnya, lihat Boxing dan Unboxing.

Gunakan kata kunci struktur untuk membuat jenis nilai kustom Anda sendiri. Biasanya, struct digunakan sebagai kontainer untuk sekumpulan kecil variabel terkait, seperti yang ditunjukkan dalam contoh berikut:

public struct Coords(int x, int y)
{
    public int X { get; init; } = x;
    public int Y { get; init; } = y;
}

Untuk informasi selengkapnya tentang struktur, lihat Jenis struktur. Untuk informasi selengkapnya tentang jenis nilai, lihat Jenis nilai.

Kategori jenis nilai lainnya adalah enum. Enum mendefinisikan sekumpulan konstanta integral bernama. Misalnya, enumerasi System.IO.FileMode di pustaka kelas .NET berisi sekumpulan bilangan bulat konstanta bernama yang menentukan bagaimana file harus dibuka. Ini didefinisikan seperti yang ditunjukkan dalam contoh berikut:

public enum FileMode
{
    CreateNew = 1,
    Create = 2,
    Open = 3,
    OpenOrCreate = 4,
    Truncate = 5,
    Append = 6,
}

Konstanta System.IO.FileMode.Create memiliki nilai 2. Namun, namanya jauh lebih bermakna bagi manusia yang membaca kode sumber, dan untuk alasan itu lebih baik menggunakan enumerasi alih-alih angka harfiah yang konstan. Untuk informasi selengkapnya, lihat System.IO.FileMode .

Semua enum mewarisi dari System.Enum, yang mewarisi dari System.ValueType. Semua aturan yang berlaku untuk struktur juga berlaku untuk enum. Untuk informasi selengkapnya tentang enum, lihat Jenis enumerasi.

Jenis referensi

Jenis yang Anda tentukan sebagai class, record class, record, delegate, array, atau interface adalah reference type.

Saat Anda mendeklarasikan variabel reference type, variabel tersebut berisi nilai null sampai Anda menetapkannya dengan suatu instance dari jenis itu atau membuatnya menggunakan operator new. Contoh berikut menunjukkan pembuatan dan penetapan kelas:

MyClass myClass = new();
MyClass myClass2 = myClass;

Anda tidak dapat langsung menginstansiasi interface dengan menggunakan operator new. Sebagai gantinya, buat dan tetapkan instans kelas yang mengimplementasikan antarmuka. Pertimbangkan contoh berikut:

MyClass myClass = new();

// Declare and assign using an existing value.
IMyInterface myInterface = myClass;

// Or create and assign a value in a single statement.
IMyInterface myInterface2 = new MyClass();

Saat Anda membuat objek, sistem mengalokasikan memori pada tumpukan terkelola. Variabel hanya menyimpan referensi ke lokasi objek. Jenis pada tumpukan terkelola memerlukan overhead baik saat dialokasikan maupun saat diambil kembali. Pengumpulan sampah adalah fungsionalitas manajemen memori otomatis CLR, yang melakukan reklamasi. Namun, pengumpulan sampah juga sangat dioptimalkan, dan dalam sebagian besar skenario tidak menciptakan masalah performa. Untuk informasi selengkapnya tentang pengumpulan sampah, lihat Manajemen Memori Otomatis.

Semua array adalah jenis referensi, bahkan jika elemennya adalah jenis nilai. Arrays secara implisit berasal dari kelas System.Array. Anda mendeklarasikan dan menggunakannya dengan menggunakan sintaks yang disederhanakan yang disediakan C#, seperti yang ditunjukkan dalam contoh berikut:

// Declare and initialize an array of integers.
int[] nums = [1, 2, 3, 4, 5];

// Access an instance property of System.Array.
int len = nums.Length;

Jenis referensi sepenuhnya mendukung pewarisan. Saat Anda membuat kelas, Anda dapat mewarisi dari antarmuka atau kelas lain yang tidak didefinisikan sebagai tertutup. Kelas lain dapat mewarisi dari kelas Anda dan mengambil alih metode virtual Anda. Untuk informasi selengkapnya tentang cara membuat kelas Anda sendiri, lihat Kelas, struktur, dan rekaman. Untuk informasi selengkapnya tentang pewarisan dan metode virtual, lihat Warisan.

Jenis nilai harfiah

Di C#, pengkompilasi menetapkan jenis ke nilai harfiah. Anda dapat menentukan bagaimana harfiah numerik harus di ketik dengan menambahkan huruf ke akhir angka. Misalnya, untuk menentukan bahwa nilai 4.56 harus diperlakukan sebagai float, tambahkan "f" atau "F" setelah angka: 4.56f. Jika Anda tidak menambahkan huruf, kompiler akan menentukan tipe untuk literal. Untuk informasi selengkapnya tentang jenis mana yang dapat Anda spesifikasikan menggunakan akhiran huruf, lihat Bilangan numerik integral dan Bilangan numerik floating-point.

Karena literal memiliki tipe, dan semua jenis pada akhirnya berasal dari System.Object, Anda dapat menulis dan mengkompilasi kode seperti berikut:

string s = "The answer is " + 5.ToString();
// Outputs: "The answer is 5"
Console.WriteLine(s);

Type type = 12345.GetType();
// Outputs: "System.Int32"
Console.WriteLine(type);

Tipe Generik

Deklarasikan tipe dengan satu atau beberapa parameter tipe yang bertindak sebagai tempat penampung untuk tipe aktual ( tipe konkret). Kode klien menyediakan jenis konkret saat menciptakan instance dari jenis tersebut. Jenis ini disebut jenis generik. Misalnya, jenis System.Collections.Generic.List<T> .NET memiliki satu parameter jenis yang menurut konvensi diberi nama T. Saat Anda membuat instans jenis, Anda menentukan jenis objek yang dimuat daftar, seperti string:

List<string> stringList = new List<string>();
stringList.Add("String example");
// compile time error adding a type other than a string:
stringList.Add(4);

Menggunakan parameter jenis memungkinkan untuk menggunakan kembali kelas yang sama untuk menahan semua jenis elemen, tanpa harus mengonversi setiap elemen menjadi objek. Kelas koleksi generik adalah koleksi yang di ketik dengan kuat karena pengkompilasi mengetahui jenis elemen koleksi tertentu dan dapat memunculkan kesalahan pada waktu kompilasi jika, misalnya, Anda mencoba menambahkan bilangan bulat ke stringList objek dalam contoh sebelumnya. Untuk informasi selengkapnya, lihat Generik.

Tipe tuple dan anonim

Membuat jenis untuk sekumpulan nilai terkait yang sederhana bisa tidak nyaman jika Anda tidak ingin menyimpan atau meneruskan nilai-nilai ini menggunakan API publik. Anda dapat membuat tuple atau jenis anonim untuk tujuan ini. Untuk informasi selengkapnya, lihat tuple dan Jenis Anonim.

Jenis nilai yang dapat diubah ke null

Jenis nilai biasa tidak dapat memiliki nilai null. Namun, Anda dapat membuat tipe nilai nullable dengan menambahkan ? setelah tipe. Misalnya, int? adalah int jenis yang juga dapat memiliki nilai null. Tipe nilai nullable adalah instans dari tipe generik System.Nullable<T> struct. Jenis nilai nullable sangat berguna ketika Anda meneruskan data ke dan dari database yang mungkin memiliki nilai numerik null. Untuk informasi selengkapnya, lihat Jenis nilai nullable.

Deklarasi jenis implisit

Ketik variabel lokal secara implisit (tetapi bukan anggota kelas) dengan menggunakan var kata kunci. Variabel masih menerima jenis pada waktu kompilasi, tetapi pengkompilasi menyediakan jenisnya. Untuk informasi selengkapnya, lihat Variabel Lokal Berjenis Implisit.

Jenis waktu kompilasi dan jenis waktu jalan

Variabel dapat memiliki jenis waktu kompilasi dan run-time yang berbeda. Jenis waktu kompilasi adalah jenis variabel yang dideklarasikan atau disimpulkan dalam kode sumber. Tipe saat berjalan adalah tipe dari instance yang dikaitkan dengan variabel tersebut. Seringkali kedua jenis tersebut sama, seperti dalam contoh berikut:

string message = "This is a string of characters";

Dalam kasus lain, jenis waktu kompilasi berbeda, seperti yang ditunjukkan dalam dua contoh berikut:

object anotherMessage = "This is another string of characters";
IEnumerable<char> someCharacters = "abcdefghijklmnopqrstuvwxyz";

Dalam kedua contoh sebelumnya, jenis run-time adalah string. Jenis waktu kompilasi berada object di baris pertama, dan IEnumerable<char> di baris kedua.

Jika kedua jenis berbeda untuk variabel, penting untuk dipahami ketika jenis waktu kompilasi dan jenis run-time berlaku. Jenis waktu kompilasi menentukan semua tindakan yang dilakukan pengkompilasi. Tindakan pengkompilasi ini mencakup resolusi panggilan metode, resolusi kelebihan beban, dan pemeran implisit dan eksplisit yang tersedia. Jenis run-time menentukan semua tindakan yang diselesaikan pada waktu proses. Tindakan run-time ini mencakup pengiriman panggilan metode virtual, evaluasi is dan switch ekspresi, dan API pengujian jenis lainnya. Untuk lebih memahami bagaimana kode Anda berinteraksi dengan jenis, kenali tindakan mana yang berlaku untuk jenis mana.

Untuk informasi lebih lanjut, baca artikel berikut:

Spesifikasi bahasa C#

Untuk informasi selengkapnya, lihat Spesifikasi Bahasa C#. Spesifikasi bahasa adalah sumber definitif untuk sintaks dan penggunaan C#.