Sistem jenis C#

C# adalah bahasa dengan jenis yang kuat. Setiap variabel dan konstanta memiliki jenis, seperti halnya setiap ekspresi yang mengevaluasi ke nilai. Setiap deklarasi metode menentukan nama, jenis dan macam (nilai, referensi, atau output) untuk setiap parameter input dan untuk nilai kembalian. Pustaka kelas .NET mendefinisikan jenis numerik bawaan dan jenis kompleks yang mewakili beragam konstruksi. Konstruksi ini termasuk sistem file, koneksi jaringan, koleksi dan array objek, dan tanggal. Program C# yang umum menggunakan jenis dari pustaka kelas dan jenis yang ditentukan-pengguna yang memodelkan konsep yang khusus untuk domain masalah program.

Informasi yang disimpan dalam jenis bisa menyertakan item berikut ini:

  • Ruang penyimpanan yang diperlukan variabel jenis.
  • Nilai maksimum dan minimum yang dapat diwakilinya.
  • Anggota (metode, bidang, peristiwa, dan sebagainya) yang dikandungnya.
  • Jenis dasar yang diwarisinya.
  • Antarmuka yang diterapkannya.
  • Jenis operasi yang diizinkan.

Pengkompilasi menggunakan informasi jenis untuk memastikan semua operasi yang dilakukan dalam kode Anda berjenis aman. Misalnya, jika Anda mendeklarasikan variabel jenis int, pengkompilasi akan mengizinkan Anda untuk menggunakan variabel tersebut dalam operasi tambahan dan pengurangan. Jika Anda mencoba melakukan operasi yang sama pada variabel jenis bool, pengkompilasi akan 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;

Catatan

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

Pengkompilasi menyematkan informasi jenis ini ke dalam file yang dapat dieksekusi sebagai metadata. Runtime bahasa umum (CLR) menggunakan metadata tersebut pada run time 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 kata kunci var untuk membiarkan pengkompilasi menyimpulkan jenisnya. Contoh berikut menunjukkan beberapa deklarasi variabel yang menggunakan jenis numerik bawaan dan jenis kompleks yang ditentukan pengguna:

// 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;

Jenis parameter metode dan nilai pengembalian ditentukan 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 dinyatakan. Misalnya, Anda tidak dapat mendeklarasikan int dan kemudian menetapkannya nilai Boolean dari true. Namun, nilai dapat dikonversi ke jenis lain, misalnya ketika ditetapkan ke variabel baru atau diteruskan sebagai argumen metode. Konversi jenis yang tidak menyebabkan kehilangan data dilakukan secara otomatis oleh pengkompilasi. Konversi yang bisa menyebabkan kehilangan data memerlukan transmisi dalam kode sumber.

Untuk informasi selengkapnya, lihat Konversi Transmisi dan Jenis.

Tag bawaan

C# menyediakan sekumpulan jenis bawaan standar. Jenis ini mewakili bilangan bulat, nilai titik mengambang, ekspresi Boolean, karakter teks, nilai desimal, dan jenis data lainnya. Ada juga jenis bawaan string dan object. Jenis-jenis ini tersedia untuk Anda gunakan dalam program C# apa pun. Untuk daftar lengkap jenis bawaan, lihat Jenis bawaan.

Jenis kustom

Anda menggunakan konstruksi struct, class, interface, enum, dan record untuk membuat jenis kustom Anda sendiri. Pustaka kelas .NET itu sendiri merupakan 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. Yang lain akan tersedia hanya ketika Anda secara eksplisit menambahkan referensi proyek ke rakitan yang mendefinisikannya. Setelah pengkompilasi memiliki referensi ke rakitan tersebut, Anda dapat mendeklarasikan variabel (dan konstanta) dari jenis yang dideklarasikan pada rakitan dalam kode sumber. Untuk informasi selengkapnya, lihat Pustaka Kelas .NET.

Sistem jenis umum

Penting untuk memahami dua poin mendasar tentang sistem jenis di .NET:

  • Ini mendukung prinsip pewarisan. 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 juga bisa berasal dari jenis yang lain, dalam hal ini jenis turunannya akan mewarisi anggota dari kedua jenis dasar dalam hierarki pewarisannya. Semua jenis, termasuk jenis numerik bawaan seperti System.Int32 (kata kunci C#: int), pada akhirnya berasal dari sebuah jenis dasar tunggal, yaitu System.Object (kata kunci C#: object). Hierarki jenis terpadu ini disebut Sistem Jenis Umum (CTS). Untuk informasi selengkapnya tentang pewarisan di C#, lihat Pewarisan.
  • 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 kata kunci struct adalah jenis nilai; semua jenis numerik bawaan adalah structs. Jenis yang Anda tentukan dengan menggunakan kata kunciclass atau record adalah jenis referensi. Jenis referensi dan jenis nilai memiliki aturan waktu-kompilasi yang berbeda, dan perilaku run-time yang berbeda.

Ilustrasi berikut ini memperlihatkan hubungan antara jenis nilai dan jenis referensi dalam CTS.

Screenshot that shows CTS value types and reference types.

Catatan

Anda dapat melihat bahwa jenis yang paling umum digunakan semuanya diatur dalam namespace System. Namun, namespace yang memuat suatu 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. C# 9 menambahkan rekaman, yang merupakan jenis kelas. Masing-masing pada dasarnya merupakan struktur data yang merangkum sekumpulan data dan perilaku yang bersama-sama sebagai satu unit yang logis. Data dan perilaku adalah anggota dari kelas, struktur, atau rekaman. Anggota meliputi metode, properti, peristiwa, dan sebagainya, seperti yang tercantum nanti di artikel ini.

Deklarasi kelas, struktur, atau rekaman seperti cetak biru yang digunakan untuk membuat instans atau objek pada run time. Jika Anda menentukan kelas, struct, atau rekaman bernama Person, Person adalah nama jenisnya. Jika Anda mendeklarasikan dan menginisialisasi variabel p dari jenis Person, maka p dikatakan sebagai objek atau instans dari Person. Beberapa instans dari jenis yang sama Person dapat dibuat, dan setiap instans dapat memiliki nilai yang berbeda dalam properti dan bidangnya.

Kelas adalah jenis referensi. Ketika objek jenis dibuat, variabel tempat objek ditetapkan hanya menyimpan referensi ke memori tersebut. Ketika referensi objek ditetapkan ke variabel baru, variabel baru itu mengacu pada objek asli. Perubahan yang dilakukan melalui satu variabel akan tercermin dalam variabel lain karena keduanya merujuk pada data yang sama.

Struct adalah jenis nilai. Saat struct dibuat, variabel tempat struct ditetapkan menyimpan data aktual struct. Ketika struktur ditetapkan ke variabel baru, struct disalin. Oleh karena itu, variabel baru dan variabel asli berisi dua salinan terpisah dari data yang sama. Perubahan yang dilakukan pada satu salinan tidak memengaruhi salinan lainnya.

Jenis catatan dapat berupa jenis referensi (record class) atau jenis nilai (record struct).

Secara umum, kelas digunakan untuk membuat model perilaku yang lebih kompleks. Kelas biasanya menyimpan data yang dimaksudkan untuk dimodifikasi setelah objek kelas dibuat. Structs paling cocok untuk struktur data yang kecil. Structs biasanya menyimpan data yang dimaksudkan untuk tidak diubah setelah struct dibuat. Jenis catatan adalah struktur data dengan anggota kompilator tambahan yang disintesakan. Rekaman biasanya menyimpan data yang tidak dimaksudkan untuk diubah 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 nilai variabel tersebut. Memori untuk struct dialokasikan sebaris dalam konteks apa pun variabel itu dideklarasikan. Tidak ada alokasi tumpukan terpisah atau overhead pengumpulan sampah untuk variabel jenis-nilai. Anda bisa mendeklarasikan jenis record struct yang merupakan jenis nilai dan menyertakan anggota yang disintesis untuk rekaman.

Ada dua kategori jensi nilai: struct dan enum.

Jenis numerik bawaan adalah struct, 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 nilai-nilai ini seolah-olah nilai tersebut adalah jenis non-agregat sederhana:

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

Jenis nilai disegel. Anda tidak dapat memperoleh jenis dari jenis nilai apa pun, misalnya System.Int32. Anda tidak dapat menentukan struct untuk mewarisi dari kelas atau struktur yang ditentukan pengguna karena struct hanya dapat mewarisi dari System.ValueType. Namun, struct dapat mengimplementasikan satu atau lebih antarmuka. Anda dapat mentransmisikan jenis struct ke jenis antarmuka apa pun yang diterapkannya. Transmisi ini menyebabkan operasi pengkotakan untuk mengemas struct di dalam objek jenis referensi pada tumpukan terkelola. Operasi pengkotakan terjadi saat Anda meneruskan jenis nilai ke metode yang mengambil System.Object atau jenis antarmuka apa pun sebagai parameter input. Untuk informasi selengkapnya, lihat Pengkotakan dan Pembukaan Kotak.

Anda menggunakan kata kunci struct 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
{
    public int x, y;

    public Coords(int p1, int p2)
    {
        x = p1;
        y = p2;
    }
}

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 satu set konstanta integral bernama. Misalnya, enumerasi System.IO.FileMode di pustaka kelas .NET berisi sekumpulan bilangan bulat konstanta bernama yang menentukan bagaimana file harus dibuka. Enum ditentukan seperti yang ditunjukkan pada 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 orang yang membaca kode sumber, dan karena alasan itu lebih baik menggunakan enumerasi alih-alih angka harfiah konstan. Untuk informasi selengkapnya, lihat System.IO.FileMode.

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

Jenis referensi

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

Saat mendeklarasikan variabel dari satu reference type, yang berisi nilai null hingga Anda menetapkannya dengan instans jenis tersebut atau membuatnya menggunakan operator new. Pembuatan dan penugasan kelas ditunjukkan dalam contoh berikut:

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

interface tidak dapat langsung dibuat instans-nya menggunakan operator new. Sebagai gantinya, buat dan tetapkan instans dari kelas yang mengimplementasikan antarmuka. Pertimbangkan contoh berikut:

MyClass myClass = new MyClass();

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

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

Ketika objek itu dibuat, memorinya dialokasikan pada tumpukan terkelola. Variabel hanya menyimpan satu referensi ke lokasi objek. Jenis pada tumpukan terkelola memerlukan overhead baik saat dialokasikan maupun saat direklamasi. 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. Array secara implisit berasal dari kelas System.Array. Anda mendeklarasikan dan menggunakannya dengan sintaks yang disederhanakan yang disediakan oleh C#, seperti tampak 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 membuat kelas, Anda dapat mewarisi dari antarmuka atau kelas lain yang tidak didefinisikan sebagai disegel. 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 Pewarisan.

Jenis nilai harfiah

Dalam C#, nilai harfiah menerima jenis dari pengkompilasi. Anda dapat menentukan bagaimana harfiah numerik harus diketik 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 tidak ada huruf yang ditambahkan, pengkompilasi akan menyimpulkan jenis untuk harfiah itu. Untuk informasi selengkapnya tentang jenis mana yang dapat ditentukan dengan akhiran huruf, lihat Jenis numerik integral dan jenis numerik Floating-point.

Karena literal diketik, dan semua jenis pada akhirnya berasal dari System.Object, Anda dapat menulis dan mengkompilasi kode seperti kode 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

Jenis dapat dideklarasikan dengan satu atau lebih parameter jenis yang berfungsi sebagai tempat penampung untuk jenis aktual (jenis konkret). Kode klien menyediakan jenis konkret saat membuat instans jenis tersebut. Jenis yang demikian disebut jenis generik. Misalnya, jenis .NET System.Collections.Generic.List<T> memiliki satu parameter jenis yang secara konvensi diberi nama T. Saat Anda membuat instans jenis, Anda menentukan jenis objek yang akan dimuat daftar, misalnya, string:

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

Penggunaan parameter jenis memungkinkan untuk menggunakan kembali kelas yang sama untuk menyimpan jenis elemen apa pun, tanpa harus mengonversi tiap elemen menjadi objek. Kelas koleksi generik disebut koleksi yang diketik dengan kuat karena pengkompilasi mengetahui jenis khusus dari elemen koleksi tertentu dan dapat memunculkan kesalahan pada waktu kompilasi jika, misalnya, Anda mencoba menambahkan bilangan bulat ke objek stringList dalam contoh sebelumnya. Untuk informasi selengkapnya, lihat Generik.

Jenis implisit, jenis anonim, dan jenis nilai yang dapat diubah ke null

Anda dapat mengetik variabel lokal secara implisit (tetapi bukan anggota kelas) dengan menggunakan kata kunci var. Variabel ini masih menerima jenis pada waktu kompilasi, tetapi jenisnya disediakan oleh pengkompilasi. Untuk informasi selengkapnya, lihat Variabel Lokal yang Diketik secara Implisit.

Mungkin tidak mudah untuk membuat jenis bernama untuk serangkaian nilai terkait sederhana yang tidak ingin Anda simpan atau teruskan ke di luar batas metode. Anda dapat membuat jenis anonim untuk tujuan ini. Untuk informasi lebih lanjut, lihat Jenis anonim .

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

Jenis waktu kompilasi dan jenis run-time

Variabel dapat memiliki jenis waktu kompilasi dan run-time yang berbeda. Jenis waktu kompilasi adalah jenis variabel yang dideklarasikan atau disimpulkan dalam kode sumber. Jenis run-time adalah jenis instans yang dirujuk oleh 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 adalah object di baris pertama, dan IEnumerable<char> di baris kedua.

Jika kedua jenis berbeda untuk satu variabel, penting untuk dipahami kapan jenis waktu kompilasi dan jenis run-time berlaku. Jenis waktu kompilasi menentukan semua tindakan yang dilakukan oleh pengkompilasi. Tindakan pengkompilasi ini mencakup resolusi panggilan metode, resolusi kelebihan beban, dan transmisi implisit dan eksplisit yang tersedia. Jenis run-time menentukan semua tindakan yang diselesaikan pada run time. Tindakan run-time ini termasuk mengirimkan panggilan metode virtual, mengevaluasi ekspresi is dan switch, dan API pengujian jensi lainnya. Untuk lebih memahami bagaimana kode Anda berinteraksi dengan jenis, kenali tindakan mana yang berlaku untuk masing-masing jenis.

Untuk informasi selengkapnya, baca artikel berikut:

Spesifikasi bahasa C#

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