Aturan umum yang harus diikuti adalah bahwa struct harus berupa kumpulan properti terkait yang kecil, sederhana (satu tingkat), yang tidak dapat diubah begitu dibuat; untuk hal lain, gunakan kelas.
C # bagus karena struct dan kelas tidak memiliki perbedaan eksplisit dalam deklarasi selain kata kunci yang menentukan; jadi, jika Anda merasa perlu "meningkatkan" sebuah struct ke suatu kelas, atau sebaliknya "menurunkan" sebuah kelas ke sebuah struct, itu sebagian besar masalah sederhana mengubah kata kunci (ada beberapa gotchas lainnya; struct tidak dapat memperoleh dari kelas atau tipe struct lainnya, dan mereka tidak dapat secara eksplisit mendefinisikan konstruktor tanpa parameter default).
Saya mengatakan "sebagian besar", karena hal yang lebih penting untuk diketahui tentang struct adalah bahwa, karena mereka adalah tipe nilai, memperlakukan mereka seperti kelas (tipe referensi) dapat berakhir dengan rasa sakit setengah. Khususnya, membuat sifat-sifat struktur bisa berubah bisa menyebabkan perilaku yang tidak terduga.
Misalnya, Anda memiliki kelas SimpleClass dengan dua properti, A dan B. Anda instantiate salinan kelas ini, menginisialisasi A dan B, dan kemudian meneruskan contoh ke metode lain. Metode itu selanjutnya memodifikasi A dan B. Kembali ke fungsi pemanggilan (yang membuat instance), A dan B instance Anda akan memiliki nilai yang diberikan kepada mereka oleh metode yang dipanggil.
Sekarang, Anda membuatnya menjadi sebuah struct. Properti masih bisa berubah. Anda melakukan operasi yang sama dengan sintaks yang sama seperti sebelumnya, tetapi sekarang, nilai-nilai baru A dan B tidak dalam contoh setelah memanggil metode. Apa yang terjadi? Nah, kelas Anda sekarang adalah sebuah struct, yang berarti itu adalah tipe nilai. Jika Anda meneruskan jenis nilai ke suatu metode, default (tanpa kata kunci keluar atau ref) adalah meneruskan "menurut nilai"; salinan dangkal instance dibuat untuk digunakan oleh metode, dan kemudian dihancurkan ketika metode dilakukan meninggalkan instance awal utuh.
Ini menjadi lebih membingungkan jika Anda memiliki tipe referensi sebagai anggota struct Anda (tidak dianulir, tetapi praktik yang sangat buruk dalam hampir semua kasus); kelas tidak akan dikloning (hanya referensi struct untuknya), jadi perubahan pada struct tidak akan memengaruhi objek asli, tetapi perubahan pada subkelas struct AKAN memengaruhi instance dari kode panggilan. Ini dapat dengan mudah menempatkan struct yang bisa berubah dalam kondisi yang sangat tidak konsisten yang dapat menyebabkan kesalahan jauh dari tempat masalah sebenarnya.
Untuk alasan ini, hampir setiap otoritas di C # mengatakan untuk selalu membuat struktur Anda tidak berubah; memungkinkan konsumen untuk menentukan nilai properti hanya pada konstruksi objek, dan tidak pernah menyediakan sarana apa pun untuk mengubah nilai instance itu. Bidang hanya baca, atau properti hanya dapatkan, adalah aturannya. Jika konsumen ingin mengubah nilainya, mereka dapat membuat objek baru berdasarkan nilai-nilai yang lama, dengan perubahan yang mereka inginkan, atau mereka dapat memanggil metode yang akan melakukan hal yang sama. Ini memaksa mereka untuk memperlakukan satu instance dari struct Anda sebagai satu "nilai" konseptual, tidak dapat dibagi dan berbeda dari (tetapi mungkin setara dengan) semua yang lain. Jika mereka melakukan operasi pada "nilai" yang disimpan oleh tipe Anda, mereka mendapatkan "nilai" baru yang berbeda dari nilai awal mereka,
Untuk contoh yang baik, lihat tipe DateTime. Anda tidak dapat menetapkan salah satu bidang instance DateTime secara langsung; Anda harus membuat yang baru, atau memanggil metode yang sudah ada yang akan menghasilkan contoh baru. Ini karena tanggal dan waktu adalah "nilai", seperti angka 5, dan perubahan ke angka 5 menghasilkan nilai baru yang bukan 5. Hanya karena 5 + 1 = 6 tidak berarti 5 sekarang 6 karena Anda menambahkan 1 ke dalamnya. DateTimes bekerja dengan cara yang sama; 12:00 tidak "menjadi" 12:01 jika Anda menambahkan satu menit, Anda malah mendapatkan nilai baru 12:01 yang berbeda dari 12:00. Jika ini adalah keadaan logis untuk jenis Anda (contoh konseptual yang bagus yang tidak ada di dalam. NET adalah Uang, Jarak, Berat, dan jumlah lain dari UOM di mana operasi harus memperhitungkan semua bagian dari nilai tersebut), kemudian gunakan struct dan desain yang sesuai. Dalam kebanyakan kasus lain di mana sub-item suatu objek harus bisa berubah secara independen, gunakan kelas.