Haruskah saya lebih suka properti dengan atau tanpa bidang pribadi?

16

Basis kode saya bekerja sekarang memiliki konvensi menggunakan bidang pribadi dan properti publik. Sebagai contoh, sebagian besar kelas memiliki anggota mereka didefinisikan seperti ini:

// Fields
private double _foo;
private double _bar;
private double _baz;

// Properties
public double Foo
{
    get{ return _foo; }
    set{ _foo = value; }
}

public double Bar
{
    get{ return _bar; }
    set{ _bar = value; }
}

public double Baz
{
    get{ return _baz; }
}

Saya tahu ini dapat ditulis ulang tanpa properti pribadi internal mereka:

public double Foo{ get; set; }
public double Bar{ get; set; }
public double Baz{ get; private set; }

Saya ingin masukan tentang ini:

  • Apakah ada alasan yang baik untuk memilih gaya yang lebih lama, lebih eksplisit daripada gaya yang lebih baru, lebih ringkas?
  • Haruskah saya menulis kelas baru menggunakan gaya ringkas, atau haruskah saya mencoba mencocokkan kode yang lebih lama untuk konsistensi? Apakah konsistensi cukup layak dalam hal ini untuk membenarkan format yang lebih lama?
KChaloux
sumber
Lihat pertanyaan dan jawaban ini .
Peter K.
@PeterK. Informatif. Tidak menjawab apakah saya harus khawatir tentang mempertahankan gaya program yang lain, atau apakah detail yang cukup kecil tidak menjadi masalah.
KChaloux
@KChaloux: Dipahami! Itu sebabnya itu adalah komentar, bukan jawaban. :-)
Peter K.
@PeterK. Wajar 'nuff = p
KChaloux
1
alasan lain untuk tidak mengubah adalah jika ada sesuatu yang dilewati dengan WCF - properti otomatis memiliki masalah kontrak (karena karakter yang tidak valid dalam nama bidang dukungan yang dibuat oleh .NET)
Leon

Jawaban:

16

Ada beberapa contoh di mana gaya yang disebut "lebih tua" masih diperlukan:

A: Jenis yang tidak dapat diubah menggunakan ketidakmampuan yang disediakan bahasa The readonlypengubah di C # membeku yang nilai setelah konstruksi. Tidak ada cara untuk meniru ini dengan properti yang diimplementasikan secara otomatis (belum).

public sealed class FooBarBazInator
{
    private readonly double foo;

    public FooBarBazInator(double foo)
    {
        this.foo = foo;
    }

    public double Foo
    {
        get
        {
            return this.foo;
        }
    }
}

B: Getter / setter Anda memiliki logika apa pun. WPF dan kode Silverlight (dan sejenisnya) yang terikat data ke kelas Anda akan menerapkan INotifyPropertyChangedseperti:

public class FooBarBazInator : INotifyPropertyChanged
{
    private double foo;

    public event PropertyChangedEventHandler PropertyChanged;

    public double Foo
    {
        get
        {
            return this.foo;
        }

        set
        {
            this.foo = value;
            this.RaisePropertyChanged("Foo");
        }
    }

    private void RaisePropertyChanged(string propertyName)
    {
        var propertyChanged = this.PropertyChanged;

        if (propertyChanged != null)
        {
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Selain itu, saya katakan menggunakan gaya baru. Buat kode Anda singkat dan menawarkan area permukaan yang lebih sedikit untuk di-bug. Selain itu, jika perlu diubah untuk memasukkan logika, itu tidak akan kompatibel dengan tanda tangan.

Jesse C. Slicer
sumber
2
Semua orang tampaknya setuju untuk pergi dengan gaya baru. Anda mendapatkan +1 dan jawaban kredit karena memberi tahu saya tentang perlunya bidang dengan readonlypengubah, yang saya tidak tahu. = D
KChaloux
3
Beberapa (termasuk saya) menganggap properti otomatis dengan pengakses pribadi "cukup baik" untuk properti tidak berubah. Benar, mereka mungkin tidak benar-benar abadi, tetapi Anda hanya harus berhati-hati untuk tidak menggunakan setter di luar konstruktor.
svick
2
Alasan lain adalah jika Anda ingin meneruskan nilai refyang tidak berfungsi dengan properti, maka Anda perlu bidang
stijn
1
Alat seperti Fody dapat diimplementasikan INotifyPropertyChangedtanpa perlu bidang dukungan eksplisit. github.com/Fody/PropertyChanged
Sebastian Redl
3
Perhatikan: C # 6 memungkinkan untuk "properti auto-pengambil saja" yang menyediakan dalam satu baris apa yang dilakukan contoh saya "A".
Jesse C. Slicer
6

Saya akan mengatakan bahwa memiliki bidang dukungan eksplisit hanya omong kosong tidak berguna: itu membuat kode Anda lebih lama dan lebih sulit untuk dibaca. Ini juga menambah potensi kesalahan:

public double Bar
{
    get { return _baz; }
    set { _bar = value; }
}

Saya pikir kesalahan seperti ini bisa terjadi dengan mudah dan akan sangat sulit dikenali.

Dan jika Anda menginginkan konsistensi, lakukan dengan cara lain: ubah kode yang menggunakan bidang pencadangan menjadi kode yang menggunakan properti otomatis. Ini sangat mudah jika Anda menggunakan alat refactoring seperti ReSharper (dan jika Anda tidak menggunakan sesuatu seperti itu, saya pikir Anda harus mulai).

Tentu saja, masih ada kasus di mana memiliki bidang dukungan diperlukan, tapi saya pikir itu tidak relevan dengan pertanyaan ini.

svick
sumber
Saya benar-benar melakukan itu pada beberapa bidang lama ketika kode refactoring ... itu menyebalkan.
KChaloux
1

Meskipun pertanyaannya sudah cukup lama, saya berpikir untuk menambahkan beberapa poin yang saya baca dari sebuah buku yang layak disebutkan di sini.

  1. Mesin serialisasi run-time bertahan nama diajukan dalam aliran serial. Nama bidang dukungan untuk properti yang diimplementasikan secara otomatis (AIP) ditentukan oleh kompiler, dan itu sebenarnya bisa mengubah nama bidang dukungan ini setiap kali Anda mengkompilasi ulang kode Anda, meniadakan kemampuan untuk menghapus tanda turunan dari semua jenis yang mengandung AIP. Jadi jangan gunakan AIP dengan jenis apa pun yang Anda ingin serialkan atau deserialize.

  2. Saat men-debug, Anda tidak dapat menempatkan breakpoint pada metode AIP get atau set, sehingga Anda tidak dapat dengan mudah mendeteksi ketika aplikasi mendapatkan atau mengatur properti ini. Anda dapat mengatur breakpoint pada breakpoint yang diterapkan secara manual

Ram
sumber
3
# 1 - Kebanyakan serialisator mengabaikan properti / bidang pribadi secara default; Saya tidak pernah mengalami masalah yang Anda sebutkan. # 2 - Ini diperbaiki di VS2015 dan dapat dikerjakan di VS2013. Lihat artikel Majalah Visual Studio ini .
Brian
-3

Apakah ada alasan yang baik untuk memilih gaya yang lebih lama, lebih eksplisit daripada gaya yang lebih baru, lebih ringkas?

Tidak juga. Meskipun saya berpendapat bahwa setiap kali Anda melewati properti seperti itu, Anda seharusnya baru saja menggunakan bidang publik.

Haruskah saya menulis kelas baru menggunakan gaya ringkas, atau haruskah saya mencoba mencocokkan kode yang lebih lama untuk konsistensi? Apakah konsistensi cukup layak dalam hal ini untuk membenarkan format yang lebih lama?

Dengan asumsi Anda mengabaikan saran di atas dan memiliki sifat passthrough, saya tidak akan bertujuan untuk mencocokkan gaya. Idealnya, Anda akan kembali dan mengubah gaya lama menjadi gaya baru agar konsisten, tetapi kemungkinan orang salah membaca kode itu tipis.

Telastyn
sumber
@svick - Bah. Kecuali jika Anda melakukan refleksi ke dalam tipe tidak ada perbedaan. Jika Anda secara terbuka mengekspos jenis sebagai antarmuka layanan atau pustaka, Anda akan mengekspos antarmuka yang akan memerlukan properti. OP tidak melakukan hal-hal itu.
Telastyn
2
Tidak ada alasan teknis dalam kasus terbatas ini. Jangan lupa properti berperilaku berbeda di debugger, diagram kelas, intellisense, dan hanya ketika Anda berpikir Anda tidak melakukan refleksi, kerangka kerja .NET adalah. WinForms, WPF, dan beberapa komponen lainnya menggunakan refleksi secara internal dan mereka memperlakukan properti secara terpisah, sehingga saran untuk menggunakan bidang publik, pernah, akan diterima dengan buruk oleh banyak pengembang di situs ini.
Kevin McCormick
1
@KevinMcCormick dan kerangka kerjanya berperilaku sangat baik (dari apa yang saya mengerti) dengan bidang. Ini tidak benar-benar peduli karena ada auto-sifat sekarang, tapi masalah yang lebih besar adalah membuat hal-hal yang umum di tempat pertama. Jauh, terlalu banyak orang berpikir bahwa dengan membuat benda-benda entah bagaimana membebaskan mereka dari 'jangan membuat bidang publik'.
Telastyn
2
Kerangka kerja menanganinya secara berbeda: coba gunakan bidang publik di EF POCO, ikat ke DataGridView, gunakan PropertyGrid, dll. Saya baru saja melakukan pencarian cepat di decompiler saya ke Type.GetProperties / GetProperty dan Framework membuat panggilan ke metode ini ratusan kali, tetapi tidak untuk GetField. Intinya, mereka berbeda, dan rekomendasi untuk selalu menggunakan properti untuk data publik sebagian didasarkan pada fakta ini. Ini tidak menghalangi penggunaan bidang pernah, tetapi kasus itu perlu pembenaran. Namun, poin Anda yang lain benar, mengekspos data publik dengan ceroboh selalu merupakan ide yang buruk.
Kevin McCormick