Variabel pribadi vs properti?

41

Saat menetapkan nilai ke variabel di dalam kelas sebagian besar waktu kami dihadapkan dengan dua opsi:

private string myValue;
public string MyValue
{
   get { return myValue; }
   set { myValue = value; }
}

Apakah ada konvensi yang menentukan bagaimana kita harus menetapkan nilai pada variabel di dalam kelas kita? Sebagai contoh jika saya memiliki metode di dalam kelas yang sama harus saya tetapkan menggunakan properti atau menggunakan variabel pribadi. Saya pernah melihatnya melakukan kedua cara, jadi saya bertanya-tanya apakah ini pilihan atau kinerja adalah faktor (kecil, mungkin).

Edward
sumber

Jawaban:

23

Saya akan mengambil langkah lebih jauh, dan membawanya ke 3 kasing. Meskipun ada variasi pada masing-masing, ini adalah aturan yang saya gunakan sebagian besar waktu ketika pemrograman C #.

Dalam kasus 2 & 3, selalu buka Accessor Properti (bukan variabel bidang). Dan dalam kasus 1, Anda diselamatkan bahkan dari harus membuat pilihan ini.

1.) Properti tidak berubah (diteruskan ke konstruktor, atau dibuat pada waktu konstruksi). Dalam hal ini, saya menggunakan variabel bidang, dengan properti hanya-baca. Saya memilih ini daripada setter pribadi, karena setter pribadi tidak menjamin keabadian.

public class Abc
{ 
  private readonly int foo;

  public Abc(int fooToUse){
    foo = fooToUse;
  }

  public int Foo { get{ return foo; } }
}

2.) variabel POCO. Variabel sederhana yang dapat / diatur pada ruang publik / pribadi. Dalam hal ini saya hanya akan menggunakan properti otomatis.

public class Abc
{ 
  public int Foo {get; set;}
}

3.) ViewModel mengikat properti. Untuk kelas yang mendukung INotifyPropertyChanged, saya pikir Anda memerlukan variabel bidang pribadi, dukungan.

public class Abc : INotifyPropertyChanged
{
  private int foo;

  public int Foo
  {
    get { return foo; }
    set { foo = value;  OnPropertyChanged("foo"); }
  }
}
Sheldon Warkentin
sumber
2
+1 untuk contoh MVVM. Sebenarnya itulah yang memicu pertanyaan itu.
Edward
4
+1: Campurkan 2/3 dengan AOP dan Anda memiliki cara hebat menggunakan INPC. [Beri tahu] public int Foo {get; set; }
Steven Evers
1
@ Job Untuk kelas mana saja yang mengakses kelas, setter pribadi sudah cukup untuk kekekalan. Namun, di dalam kelas, setter pribadi tidak mencegah pengaturan nilai berulang, setelah konstruksi awal. Fitur bahasa seperti 'set baca baca pribadi' mungkin secara konseptual mengatasi masalah ini, tetapi tidak ada.
Sheldon Warkentin
1
Saya baru mengenal C #, jadi beri tahu saya, mengapa menggunakan public int Foo {get; set;}bukan public int Foo?
1
Jika kelas atau struct akan berperilaku sebagai POCO atau PODS, apa keuntungan nyata dari membungkus bidang dalam properti? Saya sepenuhnya memahami bahwa membungkus bidang dalam properti berguna ketika kelas atau struct membutuhkan, atau mungkin di masa depan perlu, untuk mempertahankan invarian sehubungan dengan isinya (mungkin dengan memastikan objek lain diperbarui agar sesuai dengan mereka), tetapi jika kelas atau struct menetapkan bahwa konsumen dapat menulis nilai apa pun dalam urutan apa pun tanpa batasan atau efek samping, perilaku bermanfaat apa yang dapat ditambahkan ke pengakses anggota?
supercat
18

Secara umum, saya akan mengatakan menetapkan ke bidang di konstruktor dan menggunakan properti di tempat lain. Dengan cara ini, jika seseorang menambahkan fungsionalitas ke properti, Anda tidak akan melewatkannya di mana pun.

Ini jelas bukan faktor kinerja. Pengoptimal akan memberikan garis sederhana atau set untuk Anda dan kode MSIL akhir mungkin akan sama.

pdr
sumber
Adakah alasan khusus untuk menggunakan bidang dalam konstruktor? Lebih sedikit kemungkinan efek samping yang aneh?
Tanda
4
@Sign: Dugaan saya adalah bahwa jika ada validasi pada properti (sekarang atau di masa depan), Anda tidak ingin menjalankan risiko gagal validasi selama konstruksi. Validasi tidak logis pada tahap ini karena objek tidak dapat dijamin stabil hingga konstruktor selesai.
Steven Evers
@Sign: Baik apa yang Anda katakan dan apa yang Snorfus katakan. Atau jika saya ingin mencatat perubahan pada properti, saya kemungkinan tidak ingin mencatat pengaturan awal. Tapi, saya memang mengatakan "secara umum".
pdr
3
@ Tanda: masalahnya adalah: jika metode set properti dapat ditimpa dalam subkelas Anda dapat memiliki efek samping selama pembuatan objek atau objek yang tidak konsisten (yaitu: properti yang diganti diprogram untuk tidak menetapkan nilai untuk bidang itu). Menggunakan properti di konstruktor hanya aman jika metode yang ditetapkan bersifat pribadi atau jika kelas disegel.
Diego
4

Tergantung.

Pertama, Anda harus memilih properti otomatis bila memungkinkan:

public string MyValue {get;set;}

Kedua, pendekatan yang lebih baik mungkin adalah dengan menggunakan properti, jika Anda memiliki logika di sana, Anda mungkin harus melewatinya sendiri, terutama jika logika itu adalah sinkronisasi ulir.

Tetapi Anda juga harus mempertimbangkan bahwa itu mungkin menghambat kinerja Anda (sedikit), jika Anda menyinkronkan dengan salah, Anda bisa menemui jalan buntu, dan kadang-kadang jalan yang benar adalah berkeliling logika di properti.

AK_
sumber
3
Saya juga suka public string MyValue {get; private set;}.
Pekerjaan
3

Yah, pendekatan langsung ke titik adalah dengan hanya menugaskannya ke variabel itu sendiri, karena Anda berada di dalam metode kelas dan Anda mengendalikan perilaku kelas.

Tetapi inti dari properti adalah bahwa mereka memisahkan variabel dari variabel abstrak. Sedangkan properti sederhana seperti dalam contoh Anda sama sekali tidak digunakan hanya atas variabel anggota publik sederhana, properti biasanya melakukan (atau seharusnya melakukan) hal-hal tambahan di dalam pengambil dan setter mereka. Dan jika Anda ingin hal-hal ini dilakukan secara otomatis ketika mengubah properti di dalam kelas, maka tentu saja lebih baik untuk bekerja di properti alih-alih variabel untuk tidak harus mengubah setiap tugas variabel ketika perilaku pengaturan properti berubah.

Anda hanya perlu memikirkannya secara konseptual. Properti sebenarnya adalah pegangan untuk mengakses beberapa keadaan internal objek, yang dapat terdiri dari lebih dari satu variabel anggota. Jadi, Anda harus bertanya pada diri sendiri apakah Anda ingin mengubah keadaan internal yang mendasarinya saja (atau hanya sebagian saja) atau properti abstrak yang mewakili keadaan ini secara keseluruhan, dan sering kali memang yang terakhir karena Anda biasanya ingin objek Anda selalu memiliki keadaan yang konsisten.

Kata Chris Reinstate Monica
sumber
2

Jika ada kemungkinan bahwa properti tersebut mendapatkan / mengatur implementasi akan berubah kadang-kadang nanti (misalnya, Anda ingin meningkatkan suatu peristiwa saat memanggil set, atau Anda akan menambahkan beberapa mekanisme evaluasi malas kemudian ke getfungsi Anda ), maka itu mungkin ide yang bagus bahwa kode Anda di dalam kelas akan menggunakan properti di hampir semua kasus kecuali untuk - kasus yang paling mungkin jarang terjadi - di mana Anda secara eksplisit tidak ingin acara atau mekanisme evaluasi malas digunakan.

Bagaimanapun, apa pun yang akan Anda lakukan, ada peluang bagus bahwa ketika Anda mengubah implementasi properti nanti sedemikian rupa, Anda harus melihat semua tempat di dalam kelas Anda mengakses properti itu untuk memeriksa apakah benar-benar properti itu akan diakses atau variabel pribadi harus digunakan.

Doc Brown
sumber
2

Saya selalu menggunakan properti publik.

Seringkali beberapa logika yang harus selalu berjalan ketika properti diatur ditambahkan ke setmetode properti, dan pengaturan bidang pribadi sebagai gantinya setter publik akan memotong logika apa pun di sana.

Anda memiliki komentar tentang MVVM yang mengarah ke pertanyaan ini, dan saya merasa ini bahkan lebih penting ketika bekerja dengan MVVM. Banyak objek yang meningkatkan PropertyChangenotifikasi setter, dan objek lain dapat berlangganan acara ini untuk melakukan beberapa tindakan ketika properti tertentu berubah. Jika Anda mengatur variabel pribadi, tindakan ini tidak akan pernah dijalankan kecuali jika Anda juga meningkatkan PropertyChangedacara secara manual .

Rachel
sumber
+1 Ya dalam kebanyakan kasus (MVVM) acara PropertyChanged adalah suatu keharusan. Dan itu hanya bisa dipecat di dalam properti. Penjelasan yang bagus.
Edward
1

Secara umum, terserah Anda apa yang harus Anda lakukan dengan properti dan bidang dukungannya saat mendapatkan / pengaturan.

Paling sering, hanya agar konsisten di seluruh kode, Anda harus menggunakan pengakses publik di mana pun mereka tersedia dan sesuai. Itu memungkinkan Anda untuk melakukan refactor dengan perubahan kode minimal; jika metode yang melakukan pengaturan ini perlu dikeluarkan dari kelas dan diletakkan di tempat lain di mana bidang dukungan tidak lagi tersedia (seperti kelas dasar), siapa yang peduli? Anda menggunakan sesuatu yang tersedia di mana pun kelas itu sendiri untuk melakukan pekerjaan itu. Bidang dukungan, dalam banyak kasus, adalah detail implementasi; tidak seorang pun di luar kelas Anda yang seharusnya tahu itu ada.

Situasi utama yang dapat saya pikirkan ketika Anda harus menggunakan bidang dukungan dan BUKAN pengakses properti adalah ketika pengakses memiliki logika tambahan (validasi, atau memperbarui informasi status lainnya di kelas) yang tidak ingin Anda jalankan. Populasi awal suatu objek adalah contoh; Anda mungkin memiliki kelas yang menggunakan dua nilai properti untuk menghitung yang ketiga, yang juga disimpan di bidang dukungan (untuk alasan kegigihan). Ketika menginisialisasi salinan baru dari objek yang diberikan data dari DB, accessor properti yang masing-masing menghitung ulang nilai ketiga dapat mengeluh jika nilai yang dibutuhkan lainnya tidak ditetapkan. Dengan menggunakan bidang dukungan untuk menetapkan nilai awal dari dua (atau tiga) properti ini, Anda melewati logika validasi / kalkulasi hingga instance berada dalam kondisi yang cukup konsisten agar logika berfungsi normal.

KeithS
sumber
0

Selalu gunakan yang masuk akal. Ya, saya tahu itu kedengarannya sangat palsu sampai-sampai tidak ada jawaban.

Inti dari properti adalah untuk menyediakan antarmuka di mana Anda dapat dengan aman mengakses model data. Untuk sebagian besar situasi, Anda selalu ingin mengakses model data dengan aman melalui antarmuka itu, seperti:

public Foo Bar
{
  get { return _bar; }
  set { _bar = doSomethingTo(value); }
}

Namun dalam situasi lain, Anda mungkin hanya menggunakan properti sebagai tampilan model data:

public Double SomeAngleDegrees
{
  get { return SomeAngleRadians * 180 / PI; }
  set { SomeAngleRadians = value * PI / 180; }
}

Jika masuk akal untuk menggunakan bentuk radian SomeAngle, maka tentu saja gunakanlah itu.

Pada akhirnya, pastikan untuk minum kool-aid Anda sendiri. Api yang menghadap ke publik Anda harus cukup tangguh untuk bekerja secara internal.

zzzzBov
sumber