Apakah ada alasan untuk menggunakan properti pribadi di C #?

245

Saya baru menyadari bahwa konstruk properti C # juga dapat digunakan dengan pengubah akses pribadi :

private string Password { get; set; }

Meskipun ini secara teknis menarik, saya tidak bisa membayangkan kapan saya akan menggunakannya karena bidang pribadi bahkan melibatkan lebih sedikit upacara :

private string _password;

dan saya tidak bisa membayangkan kapan saya harus bisa mendapatkan secara internal tetapi tidak mengatur atau mengatur tetapi tidak mendapatkan bidang pribadi:

private string Password { get; }

atau

private string Password { set; }

tapi mungkin ada kasus penggunaan dengan kelas bersarang / mewarisi atau mungkin di mana get / set mungkin berisi logika, bukan hanya memberikan kembali nilai properti, meskipun saya cenderung menjaga properti sangat sederhana dan membiarkan metode eksplisit melakukan logika apa pun, mis GetEncodedPassword().

Apakah ada yang menggunakan properti pribadi dalam C # untuk alasan apa pun atau hanya salah satu dari konstruksi kode yang secara teknis memungkinkan namun jarang digunakan?

Tambahan

Jawaban yang bagus, membacanya, saya menggunakan ini untuk properti pribadi:

  • ketika bidang pribadi harus dimuat dengan malas
  • ketika bidang pribadi membutuhkan logika tambahan atau nilai yang dihitung
  • karena bidang pribadi bisa sulit untuk di-debug
  • untuk "memberikan kontrak kepada diri sendiri"
  • untuk secara internal mengkonversi / menyederhanakan properti yang terbuka sebagai bagian dari serialisasi
  • membungkus variabel global untuk digunakan di dalam kelas Anda
Edward Tanguay
sumber
Teknik yang didorong oleh properti pribadi adalah enkapsulasi diri - lihat: sourcemaking.com/refactoring/self-encapsulate-field
LBushkin

Jawaban:

212

Saya menggunakannya jika saya perlu menyimpan nilai dan ingin memuatnya dengan malas.

private string _password;
private string Password
{
    get
    {
        if (_password == null)
        {
            _password = CallExpensiveOperation();
        }

        return _password;
    }
}
Shaun Bowe
sumber
42
pola umum yang bagus untuk inireturn _password ?? (_password = CallExpensiveOperation());
Marc
1
@EvAlex Malas <T> mungkin lebih disukai jika Anda dapat menggunakannya. Tetapi itu hanya dapat menggunakan hal-hal statis, sehingga Anda tidak dapat mengakses metode (non-statis) atau anggota lain. By the way, saya lebih suka sintaks baris tunggal return _password ?? (_password = CallExpensiveOperation());sejak beberapa saat. Atau sebenarnya Resharper lebih suka :).
Bart
4
@ Markc sejak C # 6 Anda bahkan tidak perlu menulis mendapatkan dan kembali. private string Password => _password ?? (_password = CallExpensiveOperation());
Otto Abnormalverbraucher
1
Dengan C # 8 bahkan lebih pendek:private string Password => _password ??= CallExpensiveOperation();
Dave M
2
@Bas Itu bukan pemuatan malas karena CallExpensiveOperation();disebut selama konstruksi / inisialisasi objek yang mengandung dan bukan ketika properti pertama kali diakses.
Stefan Podskubka
142

Penggunaan utama ini dalam kode saya adalah inisialisasi malas, seperti yang telah disebutkan orang lain.

Alasan lain untuk properti pribadi di atas bidang adalah bahwa properti pribadi jauh, jauh lebih mudah untuk di-debug daripada bidang pribadi. Saya sering ingin mengetahui hal-hal seperti "bidang ini diset secara tak terduga; siapa penelepon pertama yang menetapkan bidang ini?" dan itu jauh lebih mudah jika Anda bisa meletakkan breakpoint pada setter dan tekan go. Anda bisa memasukkan logging di sana. Anda dapat memasukkan metrik kinerja di sana. Anda bisa memasukkan cek konsistensi yang berjalan di build debug.

Pada dasarnya, ia datang ke: kode jauh lebih kuat daripada data . Setiap teknik yang memungkinkan saya menulis kode yang saya butuhkan adalah teknik yang bagus. Bidang jangan biarkan Anda menulis kode di dalamnya, properti lakukan.

Eric Lippert
sumber
5
Apakah "kode jauh lebih kuat daripada data" kata Anda? Googling mengembalikan referensi yang mengarah ke Anda. Hanya ingin tahu sehingga saya dapat mengutipnya dengan benar ketika saya membutuhkannya.
Joan Venge
24
@ Joan: Saya tidak tahu. Entah saya mengada-ada, atau saya mendengar orang lain mengatakannya dan berpikir, "Wah, saya harus benar-benar mencuri itu, dan kemudian melupakan semua tentang siapa saya mencuri itu."
Eric Lippert
1
+ CodeLens memberi tahu Anda di mana properti direferensikan
UuDdLrLrSs
43

mungkin ada kasus penggunaan dengan kelas bersarang / warisan atau di mana get / set mungkin berisi logika, bukan hanya memberikan kembali nilai properti

Saya pribadi menggunakan ini bahkan ketika saya tidak membutuhkan logika pada pengambil atau penyetel properti. Menggunakan properti, bahkan properti pribadi, memang membantu membuktikan kode Anda di masa depan sehingga Anda dapat menambahkan logika ke pengambil nanti, jika diperlukan.

Jika saya merasa bahwa suatu properti pada akhirnya mungkin memerlukan logika tambahan, saya kadang-kadang akan membungkusnya menjadi properti pribadi alih-alih menggunakan bidang, jadi saya tidak perlu mengubah kode saya nanti.


Dalam kasus semi-terkait (meskipun berbeda dari pertanyaan Anda), saya sangat sering menggunakan setter pribadi di properti publik:

public string Password 
{
    get; 
    private set;
}

Ini memberi Anda pengambil publik, tetapi membuat setter tetap pribadi.

Reed Copsey
sumber
+1 masuk akal: "Jika saya merasa bahwa suatu properti pada akhirnya mungkin membutuhkan logika tambahan, saya kadang-kadang akan membungkusnya menjadi properti pribadi alih-alih menggunakan bidang, jadi saya tidak perlu mengubah kode saya nanti."
Edward Tanguay
7
setter pribadi <3
Earlz
20

Satu penggunaan yang baik untuk properti hanya dapatkan pribadi adalah nilai yang dihitung. Beberapa kali saya memiliki properti yang hanya bisa dibaca pribadi dan hanya melakukan perhitungan pada bidang lain dalam tipe saya. Itu tidak layak metode dan tidak menarik untuk kelas lain jadi milik pribadi itu.

JaredPar
sumber
20

Inisialisasi malas adalah satu tempat di mana mereka bisa rapi, misalnya

private Lazy<MyType> mytype = new Lazy<MyType>(/* expensive factory function */);

private MyType MyType { get { return this.mytype.Value; } }

// In C#6, you replace the last line with: private MyType MyType => myType.Value;

Kemudian Anda dapat menulis: di this.MyTypemana saja dan bukan this.mytype.Valuemerangkum fakta bahwa itu malas dipakai di satu tempat.

Satu hal yang memalukan adalah bahwa C # tidak mendukung pelingkupan bidang dukungan ke properti (yaitu mendeklarasikannya di dalam definisi properti) untuk menyembunyikannya sepenuhnya dan memastikan bahwa itu hanya dapat diakses melalui properti.

Greg Beech
sumber
2
Setuju akan memiliki aspek pelingkupan di sana.
Chris Marisic
5
Saya sering menggunakan teknik yang sama ini dan saya juga berharap bahwa sebuah bidang dapat di-scoping ke badan kode. Ini fitur yang bagus tapi prioritas rendah.
Eric Lippert
5
@Eric Lippert - cakupan field-declarationdalam accessor-declarationsmemiliki peringkat nomor 1 pada daftar keinginan C # saya untuk waktu yang lama. Jika Anda bisa membuatnya dirancang dan diimplementasikan dalam beberapa versi (aktual) di masa depan, maka saya akan membuatkan kue untuk Anda.
Jeffrey L Whitledge
13

Satu-satunya penggunaan yang bisa saya pikirkan

private bool IsPasswordSet 
{ 
     get
     {
       return !String.IsNullOrEmpty(_password);
     }
}
Lukasz Madon
sumber
+1 untuk kelas properti yang berguna yang dihitung dari variabel pribadi lainnya
Daren Thomas
2
Mengapa tidak menggunakan metode pribadiprivate bool IsPasswordSet() { return !String.IsNullOrEmpty(_password); }
Roman
10

Properti dan bidang tidak satu banding satu. Properti adalah tentang antarmuka kelas (apakah berbicara tentang antarmuka publik atau internalnya), sedangkan bidang adalah tentang implementasi kelas. Properti tidak boleh dilihat sebagai cara untuk hanya mengekspos bidang, mereka harus dilihat sebagai cara untuk mengekspos maksud dan tujuan kelas.

Sama seperti Anda menggunakan properti untuk memberikan kontrak kepada konsumen Anda tentang apa yang merupakan kelas Anda, Anda juga dapat memberikan kontrak kepada diri sendiri untuk alasan yang sangat mirip. Jadi ya, saya menggunakan properti pribadi ketika itu masuk akal. Kadang-kadang properti pribadi dapat menyembunyikan detail implementasi seperti pemuatan malas, fakta bahwa properti benar-benar merupakan konglomerasi dari beberapa bidang dan aspek, atau bahwa properti harus secara virtual dipakai dengan setiap panggilan (pikirkan DateTime.Now). Pasti ada saat-saat di mana masuk akal untuk menegakkan ini bahkan pada diri Anda sendiri di backend kelas.

Matt Greer
sumber
+1: "Anda juga dapat memberikan kontrak kepada diri sendiri untuk alasan yang sangat mirip" masuk akal
Edward Tanguay
8

Saya menggunakannya dalam serialisasi, dengan hal-hal seperti DataContractSerializeratau protobuf-net yang mendukung penggunaan ini ( XmlSerializertidak). Ini berguna jika Anda perlu menyederhanakan objek sebagai bagian dari serialisasi:

public SomeComplexType SomeProp { get;set;}
[DataMember(Order=1)]
private int SomePropProxy {
    get { return SomeProp.ToInt32(); }
    set { SomeProp = SomeComplexType.FromInt32(value); }
}
Marc Gravell
sumber
6

Satu hal yang saya lakukan sepanjang waktu adalah menyimpan variabel / cache "global" HttpContext.Current

private static string SomeValue{
  get{
    if(HttpContext.Current.Items["MyClass:SomeValue"]==null){
      HttpContext.Current.Items["MyClass:SomeValue"]="";
    }
    return HttpContext.Current.Items["MyClass:SomeValue"];
  }
  set{
    HttpContext.Current.Items["MyClass:SomeValue"]=value;
  }
}
Earlz
sumber
5

Saya menggunakannya setiap sekarang dan kemudian. Mereka dapat memudahkan untuk debug hal-hal ketika Anda dapat dengan mudah memasukkan breakpoint di properti atau Anda dapat menambahkan pernyataan logging dll.

Dapat juga berguna jika nanti Anda perlu mengubah jenis data dengan cara tertentu atau jika Anda perlu menggunakan refleksi.

Hans Olsson
sumber
Dito; jika ada logika yang terlibat dalam get / set, saya terkadang menggunakan properti pribadi atau dilindungi. Biasanya tergantung pada seberapa banyak logika: logika sederhana yang akan saya lakukan di properti, banyak logika saya biasanya akan menggunakan fungsi bantu. Apa pun yang membuat kode paling mudah dikelola.
TechNeilogy
5

Saya menggunakan properti pribadi untuk mengurangi kode untuk mengakses sub-properti yang sering digunakan.

    private double MonitorResolution
    {
        get { return this.Computer.Accesories.Monitor.Settings.Resolution; }
    }

Berguna jika ada banyak sub properti.

Yohanes Nurcahyo
sumber
2

Ini adalah praktik umum untuk hanya memodifikasi anggota dengan metode get / set, bahkan yang pribadi. Sekarang, logika di balik ini adalah agar Anda tahu get / set Anda selalu berperilaku dengan cara tertentu (misalnya, menembakkan peristiwa) yang tampaknya tidak masuk akal karena itu tidak akan dimasukkan dalam skema properti ... tapi kebiasaan lama sulit.

corsiKa
sumber
2

Sangat masuk akal ketika ada logika yang terkait dengan set properti atau dapatkan (pikirkan inisialisasi malas) dan properti digunakan di beberapa tempat di kelas.

Jika itu hanya bidang pendukung lurus? Tidak ada yang terlintas dalam pikiran sebagai alasan yang baik.

Marc
sumber
2

Saya tahu pertanyaan ini sudah sangat lama tetapi informasi di bawah ini tidak ada dalam jawaban saat ini.

Saya tidak bisa membayangkan kapan saya harus bisa mendapatkan internal tetapi tidak mengatur

Jika Anda menyuntikkan dependensi Anda, Anda mungkin ingin memiliki Getter di Properti dan bukan setter karena ini akan menunjukkan Properti baca. Dengan kata lain Properti hanya dapat diatur dalam konstruktor dan tidak dapat diubah oleh kode lain di dalam kelas.

Visual Studio Professional juga akan memberikan informasi tentang Properti dan bukan bidang yang membuatnya lebih mudah untuk melihat bidang apa yang sedang digunakan.

PorpField

Orang
sumber
1

Yah, karena tidak ada yang disebutkan Anda dapat menggunakannya untuk memvalidasi data atau untuk mengunci variabel.

  • Validasi

    string _password;
    string Password
    {
        get { return _password; }
        set
        {
            // Validation logic.
            if (value.Length < 8)
            {
                throw new Exception("Password too short!");
            }
    
            _password = value;
        }
    }
  • Mengunci

    object _lock = new object();
    object _lockedReference;
    object LockedReference
    { 
        get
        {
            lock (_lock)
            {
                return _lockedReference;
            }
        }
        set
        {
            lock (_lock)
            {
                _lockedReference = value;
            }
        }
    }

    Catatan: Saat mengunci referensi Anda tidak mengunci akses ke anggota objek yang direferensikan.

Referensi malas: Ketika memuat malas Anda mungkin akhirnya perlu melakukannya async yang saat ini ada AsyncLazy . Jika Anda menggunakan versi yang lebih lama daripada Visual Studio SDK 2015 atau tidak menggunakannya, Anda juga dapat menggunakan AsyncEx's AsyncLazy .

Lucas Martins Juviniano
sumber
0

Beberapa penggunaan yang lebih eksotis dari bidang eksplisit meliputi:

  • Anda perlu menggunakan refatau outdengan nilai - mungkin karena itu adalah Interlockedpenghitung
  • itu dimaksudkan untuk mewakili tata letak mendasar misalnya pada structdengan tata letak eksplisit (mungkin untuk memetakan ke dump C ++, atau unsafekode)
  • secara historis jenis ini telah digunakan dengan BinaryFormatterpenanganan lapangan otomatis (mengubah ke props otomatis mengubah nama dan dengan demikian memecah serializer)
Marc Gravell
sumber