memahami penyetel pribadi

94

Saya tidak mengerti perlunya memiliki penyetel pribadi yang dimulai dengan C # 2.

Memiliki metode penyetel untuk saya memungkinkan pengguna untuk menyetel beberapa variabel di kelas itu. Dengan demikian, kami tidak akan mengekspos variabel langsung ke pengguna. Sebaliknya kami membiarkan mereka melakukannya melalui metode penyetel publik ini.

Ini bagi saya menggunakan "enkapsulasi". Ada beberapa argumen di luar sana yang mengklaim bahwa penyetel pribadi akan membiarkan Anda menerapkan enkapsulasi.

Apakah saya tidak menggunakan enkapsulasi dengan menggunakan metode penyetel publik? Mengapa kita membutuhkan penyetel pribadi?

Apa perbedaan antara kelas yang tidak dapat diubah dan kelas dengan penyetel pribadi?

Lembah
sumber
1
Saya sangat menyukai setter pribadi waktu besar - membantu saya mengubah faktor kelas yang jelek. Mereka juga membuat tidak mungkin untuk menyatakan dan menetapkan variabel contoh non-konstan sekaligus seperti: private File settingsFile = null;dan kemudian di salah satu konstruktor: if (settingsFile == null) { settingsFile = GetSettingsFile() };. Refactoring code seperti itu terkadang membuat saya menangis :). Hanya karena Anda dapat menetapkan anggota sebelum konstruktor, tidak berarti Anda harus, karena, dengan banyak konstruktor, ini membuat SULIT untuk mengikuti logika. Penyetel pribadi memaksa Anda untuk menyetel nilai di dalam konstruktor atau nanti.
Hamish Grubijan

Jawaban:

270

Secara logis.

Kehadiran penyetel pribadi karena Anda bisa menggunakan properti otomatis:

public int MyProperty { get; set; }

Apa yang akan Anda lakukan jika Anda ingin membuatnya hanya untuk dibaca?

public int MyProperty { get; }

Oh sial!! Saya tidak dapat mengaksesnya dari kelas saya sendiri; Saya harus membuatnya seperti properti biasa:

private int myProperty;
public int MyProperty { get { return myProperty; } }

Hmm ... tapi saya kehilangan fitur "Properti Otomatis" ...

public int MyProperty { get; private set; }

AHHH .. itu lebih baik !!

ktutnik
sumber
1
Terima kasih. Ini masuk akal lagi
Dene
4
@ktutnik Terima kasih telah mengungkapkan ini seperti yang Anda lakukan. Itu juga masuk akal bagiku, sekarang!
Vivek M. Chawla
4
Jawaban yang diilustrasikan dengan sangat baik.
imnk
3
Oh crap!! I can't access it from my own classMemulai C # 6.0 ini hanya benar di luar fase inisialisasi. Lihat jawaban saya stackoverflow.com/a/34223746/198797
tsemer
1
Menambahkan jawaban # tsemer, dengan c # 6, {get; }TIDAK sama dengan { get; private set; }. Untuk cara pertama property.GetSetMethod(true)kembali nulldan yang terakhir true. Ini mengejutkan saya.
emragins
38

Penyetel pribadi berguna jika Anda memiliki properti hanya baca dan tidak ingin secara eksplisit mendeklarasikan variabel pendukung.

Begitu:

public int MyProperty
{
    get; private set;
}

sama dengan:

private int myProperty;
public int MyProperty
{
    get { return myProperty; }
}

Untuk properti yang tidak diimplementasikan secara otomatis, ini memberi Anda cara yang konsisten untuk menyetel properti dari dalam kelas Anda sehingga jika Anda memerlukan validasi, dll. Anda hanya memilikinya di satu tempat.

Untuk menjawab pertanyaan terakhir Anda, MSDN mengatakan ini pada penyetel pribadi:

Namun, untuk kelas atau struct kecil yang hanya merangkum sekumpulan nilai (data) dan memiliki sedikit atau tidak ada perilaku, disarankan untuk membuat objek tidak dapat diubah dengan mendeklarasikan set accessor sebagai private.

Dari halaman MSDN pada properti yang Diimplementasikan Otomatis

ChrisF
sumber
1
Maaf saya masih belum melihat nilai tambah karena memiliki setter pribadi. Jika kita tidak ingin membuat setter diekspos, kita hanya punya getter. Jika kita ingin menambahkan validator, kita dapat memiliki setter publik dan menambahkan validasinya. Mengapa kita membutuhkan penyetel yang tidak dapat diakses? Cara saya memahaminya adalah seperti "Ambil mobil ini tetapi Anda tidak bisa mengendarainya" mengapa Anda ingin memberi saya mobil jika saya toh tidak akan mengendarainya
Dene
@ Dene - Anda pasti bisa melakukan itu, karena itu tidak salah. Memiliki properti yang diterapkan secara otomatis tidak wajib.
ChrisF
Saya tahu tidak ada yang salah dalam melakukan cara saya mengungkapkannya. Hanya bagi saya untuk menghargai improvisasi yang dibuat oleh C # 2. Sepertinya ada banyak hype di sekitarnya tapi saya tidak bisa merasakannya atau melihat nilainya.
Dene
@ Dene, saya merindukan semua hype tentang itu. Tetapi, ketika saya akhirnya melihat bahwa hal itu dapat dilakukan, saya senang karena saya tahu cara membersihkan beberapa kelas yang panjang dari era .Net 1.1. Meskipun Anda mungkin dapat menggunakan nilai properti yang belum disetel, melakukannya kurang alami dibandingkan menggunakan nilai variabel anggota instance yang telah disetel ke null.
Hamish Grubijan
Ini menyederhanakan kode. Sama seperti properti otomatis. Banyak dari pembaruan C # berikutnya adalah tentang membuat kode lebih ringkas dan karenanya dapat dibaca. Anda masih dapat melakukan hal-hal dengan cara lain jika Anda mau - kompatibilitas ke belakang juga tampaknya menjadi tujuan besar. Saya kira ini masalah selera.
niico
18

Ini agak sederhana. Penyetel pribadi memungkinkan Anda membuat properti publik atau dilindungi hanya-baca.

Itu dia. Itulah satu-satunya alasan.

Ya, Anda dapat membuat properti read-only dengan hanya menentukan getter, tetapi dengan properti auto-implmenet Anda harus menentukan get dan set, jadi jika Anda ingin properti yang diimplementasikan otomatis menjadi read-only, Anda harus menggunakan penyetel pribadi. Tidak ada cara lain untuk melakukannya.

Memang benar bahwa Private setter tidak dibuat khusus untuk properti read-only yang diimplementasikan secara otomatis, tetapi penggunaannya sedikit lebih esoteris karena alasan lain, sebagian besar berpusat di sekitar properti read-only dan penggunaan refleksi dan serialisasi.

Erik Funkenbusch
sumber
2
Terima kasih "jika Anda ingin properti yang diterapkan secara otomatis menjadi hanya-baca, Anda harus menggunakan penyetel pribadi". Ini masuk akal bagi saya
Dene
18

Dengan pengenalan C # 6.0 dan sintaks untuk Penginisialisasi Properti Otomatis , penyetel pribadi tidak lagi diperlukan untuk properti yang hanya disetel saat inisialisasi, baik sebaris atau di dalam konstruktor.

Sintaks baru ini sekarang dikompilasi:

Properti yang diinisialisasi sebaris

public class MyClass1 {
  public string MyProperty { get; } = "Aloha!"
}

Konstruktor menginisialisasi properti

public class MyClass2 {
  public string MyProperty { get; }

  public MyClass2(string myProperty) {
    MyProperty = myProperty;
  }
}
tsemer
sumber
3
@ Ziggler, memang tidak. OP juga tidak menanyakannya. Dia hanya tidak mengerti perlunya memilikinya. Ini menjawab: "Anda tidak perlu lagi memilikinya dalam skenario ini".
tsemer
6

Saya tidak mengerti perlunya memiliki penyetel pribadi yang dimulai dengan C # 2.

Misalnya, kelas faktur memungkinkan pengguna untuk menambah atau menghapus item dari properti Item tetapi tidak mengizinkan pengguna mengubah referensi Item (yaitu, pengguna tidak dapat menetapkan properti Item ke contoh objek daftar item lain).


public class Item
{
  public string item_code;
  public int qty;

  public Item(string i, int q)
  {
    this.item_code = i;
    this.qty = q;
  }
}

public class Invoice
{
  public List Items { get; private set; }

  public Invoice()
  {
    this.Items = new List();
  }
}

public class TestInvoice
{
  public void Test()
  {
    Invoice inv = new Invoice();
    inv.Items.Add(new Item("apple", 10));

    List my_items = new List();
    my_items.Add(new Item("apple", 10));

    inv.Items = my_items;   // compilation error here.
  }
}
lauhw
sumber
+1 untuk menyoroti bahwa properti dapat dimanipulasi melalui pengambil publik, meskipun mereka memiliki penyetel pribadi.
pengguna1725145
4

Katakanlah misalnya, Anda tidak menyimpan variabel aktual melalui properti atau menggunakan nilai untuk menghitung sesuatu.

Dalam kasus seperti ini, Anda dapat membuat metode untuk melakukan penghitungan

private void Calculate(int value)
{
 //...
}

Atau Anda dapat melakukannya dengan menggunakan

public int MyProperty {get; private set;}

Dalam kasus tersebut saya akan merekomendasikan untuk menggunakan nanti, sebagai refactor properti setiap elemen anggota utuh.

Selain itu, bahkan jika Anda memetakan properti dengan Variabel. Dalam kasus seperti ini, dalam kode Anda, Anda ingin menulis seperti ini:

public int myprop;
public int MyProperty {get { return myprop;}}

... ...

this.myprop = 30;

... ...
if(this.MyProperty > 5)
   this.myprop = 40;

Kode di atas terlihat mengerikan karena programmer harus selalu berhati-hati dalam menggunakan MyProperty for Get dan myprop for Set.

Rether untuk konsistensi Anda dapat menggunakan penyetel Pribadi yang membuat Propoerty hanya baca di luar sementara Anda dapat menggunakan penyetelnya di dalam kode Anda.

abhishek
sumber
3

Saya pikir beberapa orang telah berdansa tentang ini, tetapi bagi saya, nilai setter pribadi adalah Anda dapat merangkum perilaku properti, bahkan di dalam kelas. Seperti yang dicatat abhishek, jika Anda ingin mengaktifkan peristiwa perubahan properti setiap kali properti berubah, tetapi Anda tidak ingin properti dibaca / ditulis ke publik, Anda harus menggunakan penyetel pribadi, atau Anda harus menaikkan acara di mana pun Anda memodifikasi bidang dukungan. Yang terakhir ini rawan kesalahan karena Anda mungkin lupa. Terkait, jika memperbarui nilai properti menghasilkan beberapa penghitungan yang sedang dilakukan atau bidang lain yang dimodifikasi, atau inisialisasi sesuatu yang lambat, maka Anda juga ingin membungkusnya di penyetel pribadi daripada harus mengingat untuk melakukannya di mana pun Anda membuat penggunaan bidang dukungan.

siride
sumber
2

Enkapsulasi berarti bahwa status suatu objek hanya terjadi melalui antarmuka yang ditentukan, dan karena itu kelas dapat memastikan bahwa status ini selalu valid dan sesuai dengan tujuan kelas.

Oleh karena itu, dalam beberapa kasus, sangat sesuai dengan prinsip enkapsulasi untuk hanya mengekspos bidang secara publik - semua nilai yang mungkin untuk bidang valid dengan semua nilai yang mungkin dari semua bidang lainnya, dan oleh karena itu pemrogram dapat secara aktif memutuskan untuk mengizinkan bidang tersebut untuk dimanipulasi secara bebas oleh kode luar.

Kasus-kasus ini sebagian besar terbatas pada kelas yang sebagian besar merupakan "data lama biasa". Mereka juga tidak terlalu menarik dalam hal ini, jadi cukup tentang mereka.

Dalam kasus lain, dalam bahasa lain, seseorang akan memiliki metode pengambil dan penyetel, sesuatu seperti int getId()mendapatkan nilai dan void setId(int val)memperbaruinya.

Properti memungkinkan kita menggunakan sintaks yang sama untuk membaca dan menulis melalui metode seperti yang akan kita gunakan untuk membaca dan menulis bidang. Ini adalah gula sintaksis yang baik, meski tidak vital.

(Sebenarnya, karena cara kerja refleksi dan kasus seperti DataBinder.Evalitu dapat berguna untuk memiliki properti bahkan ketika bidang akan berfungsi dengan baik, tapi itu masalah lain).

Hingga penyetel pribadi diperkenalkan (sebenarnya, yang diubah dengan C # 2 adalah sintaksis untuk memiliki penyetel pribadi dan pengambil publik atau dilindungi di blok yang sama), kita dapat memiliki metode pribadi untuk melakukan pekerjaan penyetel pribadi, jadi setter pribadi tidak terlalu diperlukan. Mereka berguna, jadi meskipun hanya gula sintaksis, mereka cukup berguna.

Enkapsulasi bukanlah masalah apakah penyetel (atau pengambil) Anda publik, pribadi, terlindungi, atau internal, tetapi masalah apakah mereka pantas . Mulailah dengan default setiap bidang menjadi pribadi (dan dalam hal ini readonly) dan kemudian jika perlu tambahkan anggota (apakah properti atau metode) yang mengubah bidang tersebut, dan pastikan bahwa objek tetap valid saat berubah . Ini memastikan bahwa kelas ' invariant disimpan, yang berarti aturan yang mendeskripsikan set status yang valid tidak pernah rusak (konstruktor juga membantu dengan memastikannya dimulai dalam status yang valid).

Adapun pertanyaan terakhir Anda, menjadi tidak berubah berarti bahwa kelas tidak memiliki penyetel publik, dilindungi atau internal dan tidak ada metode publik, dilindungi atau internal yang mengubah bidang apa pun. Ada derajat ini, di C # ada tiga derajat yang mungkin:

  1. Semua bidang instance kelas adalah readonly, bahkan kode pribadi tidak dapat mengubahnya. Itu dijamin tidak dapat diubah (apa pun yang mencoba mengubahnya tidak akan dikompilasi) dan kemungkinan pengoptimalan dapat dilakukan di balik ini.

  2. Kelas tidak dapat diubah dari luar karena tidak ada anggota publik yang mengubah apa pun, tetapi tidak dijamin oleh penggunaan readonlyuntuk tidak diubah dari dalam.

  3. Kelas tidak dapat diubah seperti yang terlihat dari luar, meskipun beberapa status berubah sebagai detail implementasi. Misal, sebuah field bisa dimoisasi, dan karena itu sementara dari luar sebuah usaha untuk mendapatkannya hanya mengambil nilai yang sama, percobaan pertama tersebut sebenarnya menghitungnya dan kemudian menyimpannya untuk diambil pada percobaan berikutnya.

Jon Hanna
sumber
1

Anda memerlukan penyetel pribadi, jika Anda ingin mendukung skenario berikut (tidak hanya untuk ini, tetapi ini harus menunjukkan satu alasan bagus): Anda memiliki Properti yang hanya dapat dibaca di kelas Anda, yaitu hanya kelas itu sendiri yang diizinkan untuk berubah itu, tetapi mungkin mengubahnya setelah membuat instance. Untuk binding, Anda harus mengaktifkan peristiwa PropertyChanged, sebaiknya ini dilakukan di penyetel properti (private). Sebenarnya, Anda bisa mengaktifkan event PropertyChanged-dari tempat lain dalam kelas, tetapi menggunakan penyetel pribadi untuk ini adalah "kewarganegaraan yang baik", karena Anda tidak mendistribusikan pemicu perubahan-properti Anda ke seluruh kelas Anda tetapi menyimpannya di properti, di tempatnya.

Simon D.
sumber
1

Ya, Anda menggunakan enkapsulasi dengan menggunakan properti, tetapi ada lebih banyak nuansa enkapsulasi daripada hanya mengambil kendali atas bagaimana properti dibaca dan ditulis. Menolak properti untuk disetel dari luar kelas dapat berguna untuk ketahanan dan performa.

Kelas yang tidak dapat diubah adalah kelas yang tidak berubah setelah dibuat, jadi penyetel pribadi (atau tidak ada penyetel sama sekali) diperlukan untuk melindungi properti.

Penyetel pribadi lebih sering digunakan dengan singkatan properti yang diperkenalkan di C # 3. Dalam C # 2 penyetel sering kali dihilangkan, dan data pribadi diakses langsung saat disetel.

Properti ini:

public int Size { get; private set; }

sama dengan:

private int _size;
public int Size {
  get { return _size; }
  private set { _size = value; }
}

kecuali, nama variabel pendukung dibuat secara internal oleh kompilator, jadi Anda tidak dapat mengaksesnya secara langsung.

Dengan properti singkatan, penyetel pribadi diperlukan untuk membuat properti hanya-baca, karena Anda tidak dapat mengakses variabel dukungan secara langsung.

Guffa
sumber
Jika saya mengingatnya dengan benar, Anda tidak dapat memiliki pengubah akses yang berbeda pada getdan setsebelum C # 2.0. Juga, saya pikir Anda mencampur 2.0 dan 3.0, karena singkatan yang diterapkan otomatis yang Anda maksud adalah 3.0.
Anthony Pegram
1

Saya tidak mengerti perlunya memiliki penyetel pribadi yang dimulai dengan C # 2.

Contoh use case:

Saya memiliki contoh objek aplikasi 'UserInfo'yang berisi properti SessionTokenIDV1yang tidak ingin saya perlihatkan kepada konsumen di kelas saya.

Saya juga membutuhkan kemampuan untuk menetapkan nilai itu dari kelas saya.

Solusi saya adalah merangkum properti seperti yang ditunjukkan dan membuat penyetel menjadi pribadi sehingga saya dapat mengatur nilai token sesi tanpa mengizinkan kode contoh untuk juga mengaturnya (atau bahkan melihatnya dalam kasus saya)

public class UserInfo
{
   public String SessionTokenIDV1 { get; set; }

}


public class Example
{
  // Private vars
  private UserInfo _userInfo = new UserInfo();

  public string SessionValidV1
  {
    get { return ((_userInfo.SessionTokenIDV1 != null) && (_userInfo.SessionTokenIDV1.Length > 0)) ? "set" : "unset"; }
    private set { _userInfo.SessionTokenIDV1 = value; }
  }
}

Edit: Edit Tag Kode Tetap: Contoh memiliki kesalahan yang telah diperbaiki

Curt Eckhart
sumber
-1

Penghargaan untuk https://www.dotnetperls.com/property .

penyetel pribadi sama dengan bidang hanya baca. Mereka hanya dapat diatur dalam konstruktor. Jika Anda mencoba mengatur dari luar Anda mendapatkan kesalahan waktu kompilasi.

public class MyClass
{
    public MyClass()
    {
        // Set the private property.
        this.Name = "Sample Name from Inside";
    }
     public MyClass(string name)
    {
        // Set the private property.
        this.Name = name;
    }
    string _name;
    public string Name
    {
        get
        {
            return this._name;
        }
        private set
        {
            // Can only be called in this class.
            this._name = value;
        }
    }
}

class Program
{
    static void Main()
    {
        MyClass mc = new MyClass();
        Console.WriteLine(mc.name);

        MyClass mc2 = new MyClass("Sample Name from Outside");
        Console.WriteLine(mc2.name);
    }
}

Silakan lihat screen shot di bawah ini ketika saya mencoba mengaturnya dari luar kelas.

masukkan deskripsi gambar di sini

Ziggler
sumber