Praktik terbaik getter, setter, dan properti. Jawa vs. C #

92

Saya mengambil kelas C # sekarang dan saya mencoba mencari cara terbaik untuk melakukan sesuatu. Saya berasal dari latar belakang Java sehingga saya hanya terbiasa dengan praktik terbaik Java; Saya pemula C #!

Di Jawa, jika saya memiliki properti pribadi, saya melakukan ini;

private String name;

public void setName(String name) {
   this.name = name;
}

public String getName() {
   return this.name;
}

Di C #, saya melihat ada banyak cara untuk melakukan ini.

Saya bisa melakukannya seperti Java:

private string name;

public void setName(string name) {
   this.name = name;
}

public string getName() {
   return this.name;
}

Atau saya bisa melakukannya dengan cara ini:

private string name;

public string Name {
   get { return name; }
   set { name = value; }
}

Atau:

public string Name { get; set; }

Manakah yang harus saya gunakan, dan peringatan atau kehalusan apa yang terlibat dengan setiap pendekatan? Saat membuat kelas, saya mengikuti praktik terbaik umum yang saya ketahui dari Java (terutama membaca Java Efektif). Jadi misalnya, saya menyukai kekekalan (menyediakan penyetel hanya jika diperlukan). Saya hanya ingin tahu bagaimana praktik ini cocok dengan berbagai cara menyediakan setter dan getter di C #; pada dasarnya, bagaimana saya menerjemahkan praktik terbaik dari dunia Java ke dalam C #?

EDIT

Saya memposting ini sebagai komentar untuk jawaban Jon Skeet tetapi kemudian menjadi panjang:

Bagaimana dengan properti non-sepele (yaitu, dengan pemrosesan dan validasi yang signifikan mungkin)? Bisakah saya tetap mengeksposnya melalui properti publik tetapi dengan logika yang dikemas dalam getdan set? Mengapa saya harus melakukan ini karena memiliki metode penyetel dan pengambil yang berdedikasi (dengan logika pemrosesan dan validasi yang terkait).

Vivin Paliath
sumber

Jawaban:

88

Pra-C # 6

Saya akan menggunakan yang terakhir ini, untuk properti yang sepele. Perhatikan bahwa saya akan menyebutnya properti publik karena pengambil dan penyetel bersifat publik.

Kekekalan agak merepotkan dengan properti yang diimplementasikan secara otomatis - Anda tidak dapat menulis properti otomatis yang hanya memiliki getter; hal terdekat yang bisa Anda datangi adalah:

public string Foo { get; private set; }

yang tidak benar - benar tidak dapat diubah ... hanya tetap di luar kelas Anda. Jadi, Anda mungkin ingin menggunakan properti hanya-baca yang sebenarnya :

private readonly string foo;
public string Foo { get { return foo; } }

Anda pasti tidak ingin menulis getName()dan setName(). Dalam beberapa kasus, masuk akal untuk menulis metode Get / Set daripada menggunakan properti, terutama jika itu bisa mahal dan Anda ingin menekankannya. Namun, Anda ingin mengikuti konvensi penamaan .NET PascalCase untuk metode, dan Anda tidak ingin properti sepele seperti ini diimplementasikan dengan metode normal - properti jauh lebih idiomatis di sini.

C # 6

Hore, akhirnya kami memiliki properti yang diterapkan secara otomatis hanya-baca:

// This can only be assigned to within the constructor
public string Foo { get; }

Demikian juga untuk sifat read-only yang melakukan perlu melakukan beberapa pekerjaan, Anda dapat menggunakan properti anggota bertubuh:

public double Area => height * width;
Jon Skeet
sumber
5
Lebih tepatnya: kode apa pun yang reiew akan menunjukkan cara java adalah peretasan yang melewati konstruksi runtime langauge AND (!) Yang valid dan mematikan penggunaan properti sebagai proeprty (yaitu object.property = "value"). Dalam beberapa tim, hal ini akan menghasilkan pembicaraan yang bagus tentang sikap - tergantung pada senioritas yang dikombinasikan dengan insentif untuk menerapkan sikap itu pada pesaing. Serius, JANGAN MELAWAN BAHASA. Terutama karena "cara" java adalah peretasan yang dipilih agar tidak mengubah bahasa untuk dukungan properti nyata.
TomTom
1
Saya kira kabar baiknya adalah tanggapan saya tampaknya tidak bertentangan dengan apa pun yang Anda sebutkan. Kabar buruknya adalah jari-jarimu jauh lebih cepat dariku. Wawasan luar biasa, dan terima kasih atas detail tambahannya.
jeremyalan
4
Untuk menjawab Anda edit: Anda dapat menggunakan metode get / set dengan sejumlah logika di dalamnya yang Anda inginkan. Kami sering melakukan ini, terutama untuk validasi. Praktik terbaik, bagaimanapun, adalah tidak memiliki banyak logika lambat (akses database misalnya), logika berbahaya (pengecualian melempar), atau mutasi (mengubah banyak status) di properti. Properti diharapkan bertindak kurang lebih seperti keadaan sederhana. Lebih dari itu harus ditunjukkan dengan menggunakan fungsi sebagai gantinya.
CodexArcanum
2
@ Artindru: Ya, saya tahu itu. Sangat mungkin untuk menulis properti hanya-baca. Tetapi Anda tidak dapat mendeklarasikan properti yang diimplementasikan secara otomatis tanpa set accessor.
Jon Skeet
2
Pada C # 6 Anda sekarang dapat memiliki properti otomatis tanpa pengakses yang ditetapkan , ( public int Y { get; } = y;).
Scott Chamberlain
17

Jika yang Anda butuhkan hanyalah variabel untuk menyimpan beberapa data:

public string Name { get; set; }

Ingin membuatnya tampak hanya-baca?

public string Name { get; private set; }

Atau bahkan lebih baik ...

private readonly string _name;

...

public string Name { get { return _name; } }

Ingin melakukan pengecekan nilai sebelum menetapkan properti?

public string Name 
{
   get { return m_name; }
   set
   {
      if (value == null)
         throw new ArgumentNullException("value");

      m_name = value;
   }
}

Secara umum, GetXyz () dan SetXyz () hanya digunakan dalam kasus tertentu, dan Anda hanya perlu menggunakan naluri saat dirasa tepat. Secara umum, saya akan mengatakan bahwa saya mengharapkan sebagian besar properti get / set tidak mengandung banyak logika dan memiliki sangat sedikit, jika ada, efek samping yang tidak terduga. Jika membaca nilai properti memerlukan pemanggilan layanan atau mendapatkan input dari pengguna untuk membangun objek yang saya minta, maka saya akan menggabungkannya ke dalam metode, dan menyebutnya seperti BuildXyz(), bukan GetXyz().

jeremyalan
sumber
2
Saya tidak akan membuang pengecualian di properti, saya lebih suka menggunakan penyetel metode dengan kontrak untuk menentukan perilaku spesifik tersebut. Jika sebuah properti bertipe int, saya berharap setiap int memenuhi syarat. Sesuatu yang membutuhkan pengecualian melempar tidak sederhana menurut saya, pemanggilan tipe INotifyPropertyChanged lebih di baris itu menurut saya.
flindeberg
3
private setter! = immutable
piedar
piedar benar. Penyetel pribadi berarti saya tidak dapat membuat tugas, tetapi saya masih dapat menggunakan myList.Add(), misalnya (selama objek terekspos untuk berubah, itu bisa berubah).
1
@flindeberg Maaf, tapi saya tidak setuju ... Bagaimana jika Anda memiliki bilah kemajuan dengan nilai Min/ Max. Anda ingin memastikannya Max > Min? Memiliki SetRange(Min, Max)kekuatan masuk akal, tetapi bagaimana Anda akan membaca kembali nilai-nilai itu? Properti Min / Max Hanya Baca? Melempar pengecualian untuk masukan yang tidak valid sepertinya cara terbersih untuk menangani ini.
Dasar
12

Gunakan properti di C #, bukan metode get / set. Mereka ada untuk kenyamanan Anda dan itu idiomatis.

Adapun dua contoh C # Anda, yang satu hanyalah gula sintaksis untuk yang lain. Gunakan properti auto jika yang Anda butuhkan hanyalah wrapper sederhana di sekitar variabel instance, gunakan versi lengkap saat Anda perlu menambahkan logika di getter dan / atau setter.

Ed S.
sumber
5

Di C # mendukung properti untuk mengekspos bidang pribadi untuk get dan / atau set. Formulir yang Anda sebutkan adalah properti otomatis di mana get dan set secara otomatis menghasilkan bidang dukungan pivot tersembunyi untuk Anda.

Saya menyukai properti otomatis jika memungkinkan tetapi Anda tidak boleh melakukan set / get metode pasangan di C #.

James Michael Hare
sumber
5
public string Name { get; set; }

Ini hanyalah properti yang diterapkan secara otomatis , dan secara teknis sama dengan properti normal. Bidang dukungan akan dibuat saat kompilasi.

Semua properti pada akhirnya akan diubah menjadi fungsi, sehingga implementasi terkompilasi yang sebenarnya pada akhirnya sama seperti yang biasa Anda lakukan di Java.

Gunakan properti yang diimplementasikan secara otomatis saat Anda tidak perlu melakukan operasi khusus di bidang dukungan. Gunakan properti biasa jika tidak. Gunakan fungsi get dan setel saat operasi memiliki efek samping atau mahal secara komputasi, gunakan properti jika tidak.

Steven Jeuris
sumber
4

Terlepas dari cara mana yang Anda pilih di C #, hasil akhirnya sama. Anda akan mendapatkan variabel backinng dengan metode pengambil dan penyetel yang terpisah. Dengan menggunakan properti, Anda mengikuti praktik terbaik dan jadi ini soal seberapa panjang Anda ingin mendapatkannya.

Secara pribadi saya akan memilih properti otomatis, versi terakhir:, public string Name { get; set; }karena properti tersebut menggunakan paling sedikit ruang. Dan Anda selalu dapat mengembangkannya di masa mendatang jika Anda perlu menambahkan sesuatu seperti validasi.

Paul Sasik
sumber
4

Jika memungkinkan, saya lebih suka publik string Name { get; set; }karena singkat dan mudah dibaca. Namun, ada kalanya hal ini diperlukan

private string name;

public string Name {
   get { return name; }
   set { name = value; }
}
SquidScareMe
sumber
2
dapatkah Anda menjelaskan kapan dan mengapa hal semacam itu perlu?
Jesper
Saat ini saya tidak dapat memikirkan alasan untuk menggunakan versi ke-2. Saya hanya mengatakan 'mungkin saat-saat ini diperlukan'. Saya benci menggunakan yang absolut kecuali saya positif.
SquidScareMe
3
Dan kenapa ini 'lolz'? Anda dapat menunjukkan dukungan Anda untuk komentar dengan upvoting.
SquidScareMe
1
Salah satu alasan untuk menggunakan bidang dukungan eksplisit adalah ketika Anda ingin melakukan operasi atom / thread-safe terhadap nilai
Dasar
4

Dalam C # cara yang lebih disukai adalah melalui properti daripada getX()dan setX()metode. Juga, perhatikan bahwa C # tidak mengharuskan properti memiliki get dan set - Anda dapat memiliki properti get-only dan properti set-only.

public boolean MyProperty
{
    get { return something; }
}

public boolean MyProperty
{
    set { this.something = value; }
}
Zach Johnson
sumber
4

Pertama izinkan saya mencoba menjelaskan apa yang Anda tulis:

// private member -- not a property
private string name;

/// public method -- not a property
public void setName(string name) {
   this.name = name;
}

/// public method -- not a property
public string getName() {
   return this.name;
}

// yes it is property structure before .Net 3.0
private string name;
public string Name {
   get { return name; }
   set { name = value; }
}

Struktur ini juga digunakan saat ini tetapi paling cocok jika Anda ingin melakukan beberapa fungsionalitas tambahan, misalnya ketika nilai ditetapkan, Anda dapat mengurai untuk memanfaatkannya dan menyimpannya di anggota pribadi untuk mengubah penggunaan internal.

Dengan .net framework 3.0

// this style is introduced, which is more common, and suppose to be best
public string Name { get; set; }

//You can more customize it
public string Name
{
    get;
    private set;    // means value could be set internally, and accessed through out
}

Semoga Anda lebih beruntung di C #

Waqas Raja
sumber
3

Seperti disebutkan, semua pendekatan ini menghasilkan hasil yang sama. Yang paling penting adalah Anda memilih konvensi dan mematuhinya. Saya lebih suka menggunakan dua contoh properti terakhir.

Jeff LaFay
sumber
2

seperti kebanyakan jawaban di sini, gunakan properti Otomatis. Intuitif, lebih sedikit baris kode dan lebih bersih. Jika Anda harus membuat kelas Anda berseri, tandai kelas [Serializable]/ dengan [DataConract]atribut. Dan jika Anda menggunakan [DataContract]tandai anggota dengan

[DataMember(Name="aMoreFriendlyName")]
public string Name { get; set; }

Penyetel pribadi atau publik tergantung pada preferensi Anda.

Perhatikan juga bahwa properti otomatis memerlukan getter dan setter (publik atau pribadi).

/*this is invalid*/
public string Name 
{ 
    get; 
   /* setter omitted to prove the point*/
}

Alternatifnya, jika Anda hanya ingin mendapatkan / menyetel, buat bidang pendukung sendiri

ram
sumber
0

Manakah yang harus saya gunakan, dan peringatan atau kehalusan apa yang terlibat dengan setiap pendekatan?

Saat menggunakan properti, ada satu peringatan yang belum disebutkan: Dengan properti, Anda tidak dapat memiliki parameterisasi getter atau setter.

Misalnya bayangkan Anda ingin mengambil item daftar dan ingin juga menerapkan filter pada saat yang bersamaan. Dengan metode get, Anda dapat menulis sesuatu seperti:

obj.getItems(filter);

Sebaliknya, dengan properti Anda terpaksa harus mengembalikan semua barang terlebih dahulu

obj.items

lalu terapkan filter di langkah berikutnya atau Anda harus menambahkan properti khusus yang mengekspos item yang difilter menurut kriteria berbeda, yang segera membengkak API Anda:

obj.itemsFilteredByX
obj.itemsFilteredByY

Apa yang terkadang bisa menjadi gangguan adalah ketika Anda memulai dengan sebuah properti, misalnya obj.itemsdan kemudian menemukan bahwa parameter-pengambil atau penyetel diperlukan atau akan mempermudah pengguna API kelas. Anda sekarang perlu menulis ulang API Anda dan mengubah semua tempat di kode Anda yang mengakses properti ini atau mencari solusi alternatif. Sebaliknya, dengan metode get, misalnya obj.getItems(), Anda cukup memperluas tanda tangan metode Anda untuk menerima objek "konfigurasi" opsional, misalnyaobj.getItems(options) tanpa harus menulis ulang semua tempat yang memanggil metode Anda.

Meskipun demikian, properti (diimplementasikan secara otomatis) di C # masih merupakan pintasan yang sangat berguna (karena berbagai alasan yang disebutkan di sini) karena sebagian besar waktu parametrikisasi mungkin tidak diperlukan - tetapi peringatan ini tetap berlaku.

B12Pemanggang
sumber