Dalam bukunya yang luar biasa, CLR Via C #, Jeffrey Richter mengatakan bahwa dia tidak menyukai properti, dan merekomendasikan untuk tidak menggunakannya. Dia memberi beberapa alasan, tapi saya tidak begitu mengerti. Adakah yang bisa menjelaskan kepada saya mengapa saya harus atau tidak boleh menggunakan properti? Di C # 3.0, dengan properti otomatis, apakah ini berubah?
Sebagai referensi, saya menambahkan pendapat Jeffrey Richter:
• Properti mungkin hanya baca atau tulis; akses lapangan selalu dapat dibaca dan ditulis. Jika Anda mendefinisikan properti, yang terbaik adalah menawarkan get dan set accessor method.
• Sebuah metode properti mungkin membuat pengecualian; akses lapangan tidak pernah membuat pengecualian.
• Properti tidak bisa dikirimkan sebagai parameter out atau ref ke metode; sebuah lapangan bisa. Misalnya, kode berikut tidak akan dikompilasi:
using System;
public sealed class SomeType
{
private static String Name
{
get { return null; }
set {}
}
static void MethodWithOutParam(out String n) { n = null; }
public static void Main()
{
// For the line of code below, the C# compiler emits the following:
// error CS0206: A property or indexer may not
// be passed as an out or ref parameter
MethodWithOutParam(out Name);
}
}
• Metode properti membutuhkan waktu lama untuk dieksekusi; akses lapangan selalu selesai dengan segera. Alasan umum untuk menggunakan properti adalah untuk melakukan sinkronisasi utas, yang dapat menghentikan utas selamanya, dan oleh karena itu, properti tidak boleh digunakan jika sinkronisasi utas diperlukan. Dalam situasi itu, metode lebih disukai. Selain itu, jika kelas Anda dapat diakses dari jarak jauh (misalnya, kelas Anda diturunkan dari System.MashalByRefObject), memanggil metode properti akan sangat lambat, dan oleh karena itu, metode lebih disukai daripada properti. Menurut pendapat saya, kelas yang diturunkan dari MarshalByRefObject tidak boleh menggunakan properti.
• Jika dipanggil beberapa kali berturut-turut, metode properti dapat mengembalikan nilai yang berbeda setiap kali; sebuah bidang mengembalikan nilai yang sama setiap kali. Kelas System.DateTime memiliki properti Sekarang hanya baca yang mengembalikan tanggal dan waktu saat ini. Setiap kali Anda menanyakan properti ini, ini akan mengembalikan nilai yang berbeda. Ini adalah kesalahan, dan Microsoft berharap mereka dapat memperbaiki kelas dengan menjadikan Now sebagai metode alih-alih properti.
• Metode properti dapat menyebabkan efek samping yang dapat diamati; akses lapangan tidak pernah melakukannya. Dengan kata lain, pengguna suatu tipe harus dapat menyetel berbagai properti yang ditentukan oleh tipe dalam urutan apa pun yang dia pilih tanpa memperhatikan perilaku yang berbeda dalam tipe tersebut.
• Sebuah metode properti mungkin memerlukan memori tambahan atau mengembalikan referensi ke sesuatu yang sebenarnya bukan bagian dari status objek, jadi memodifikasi objek yang dikembalikan tidak berpengaruh pada objek asli; membuat kueri bidang selalu mengembalikan referensi ke objek yang dijamin menjadi bagian dari status objek asli. Bekerja dengan properti yang mengembalikan salinan bisa sangat membingungkan pengembang, dan karakteristik ini sering kali tidak didokumentasikan.
sumber
Jawaban:
Alasan Jeff untuk tidak menyukai properti adalah karena terlihat seperti bidang - jadi pengembang yang tidak memahami perbedaannya akan memperlakukannya seolah-olah itu bidang, dengan asumsi bahwa mereka akan murah untuk dieksekusi, dll.
Secara pribadi saya tidak setuju dengan dia dalam hal ini - saya menemukan properti membuat kode klien lebih mudah dibaca daripada panggilan metode yang setara. Saya setuju bahwa pengembang perlu mengetahui bahwa properti pada dasarnya adalah metode yang disamarkan - tetapi menurut saya, mendidik pengembang tentang hal itu lebih baik daripada membuat kode lebih sulit untuk dibaca menggunakan metode. (Secara khusus, setelah melihat kode Java dengan beberapa getter dan setter dipanggil dalam pernyataan yang sama, saya tahu bahwa kode C # yang setara akan jauh lebih sederhana untuk dibaca. Hukum Demeter semuanya sangat baik secara teori, tetapi terkadang
foo.Name.Length
memang demikian hal yang benar untuk digunakan ...)(Dan tidak, properti yang diterapkan secara otomatis tidak benar-benar mengubah semua ini.)
Ini sedikit seperti argumen yang menentang penggunaan metode ekstensi - saya dapat memahami alasannya, tetapi manfaat praktis (bila digunakan dengan hemat) melebihi sisi negatifnya dalam pandangan saya.
sumber
Baiklah, mari kita bahas argumennya satu per satu:
Ini adalah kemenangan untuk properti, karena Anda memiliki kontrol akses yang lebih cermat.
Meskipun ini sebagian besar benar, Anda bisa memanggil metode pada bidang objek yang tidak diinisialisasi, dan memiliki pengecualian yang dilemparkan.
Adil.
Ini juga membutuhkan sedikit waktu.
Tidak benar. Bagaimana Anda tahu bahwa nilai bidang tidak berubah (mungkin oleh utas lain)?
Jika itu adalah kesalahan, itu kecil.
Adil.
Sebagian besar protes juga dapat dikatakan untuk getter dan setter Java —dan kami melakukannya cukup lama tanpa masalah seperti itu dalam praktiknya.
Saya pikir sebagian besar masalah dapat diselesaikan dengan penyorotan sintaks yang lebih baik (yaitu membedakan properti dari bidang) sehingga programmer tahu apa yang diharapkan.
sumber
_field
. Atau bahkan hanya huruf kecilfield
.Saya belum membaca bukunya, dan Anda belum mengutip bagiannya yang tidak Anda mengerti, jadi saya harus menebaknya.
Beberapa orang tidak menyukai properti karena mereka dapat membuat kode Anda melakukan hal-hal yang mengejutkan.
Jika saya mengetik
Foo.Bar
, orang yang membacanya biasanya berharap ini hanya mengakses bidang anggota kelas Foo. Ini operasi yang murah, hampir gratis, dan deterministik. Saya dapat menyebutnya berulang kali, dan mendapatkan hasil yang sama setiap saat.Sebaliknya, dengan properti, ini mungkin sebenarnya adalah panggilan fungsi. Ini mungkin putaran tak terbatas. Ini mungkin membuka koneksi database. Ini mungkin mengembalikan nilai yang berbeda setiap kali saya mengaksesnya.
Ini adalah argumen yang mirip dengan mengapa Linus membenci C ++. Kode Anda bisa mengejutkan pembaca. Dia membenci operator yang kelebihan beban:
a + b
tidak selalu berarti penambahan sederhana. Ini mungkin berarti beberapa operasi yang sangat rumit, seperti properti C #. Ini mungkin memiliki efek samping. Itu bisa melakukan apa saja.Sejujurnya, saya pikir ini argumen yang lemah. Kedua bahasa itu penuh dengan hal-hal seperti ini. (Haruskah kita menghindari operator overloading di C # juga? Bagaimanapun, argumen yang sama dapat digunakan di sana)
Properti memungkinkan abstraksi. Kita dapat berpura - pura bahwa sesuatu adalah bidang biasa, dan menggunakannya seolah-olah itu adalah satu bidang, dan tidak perlu khawatir tentang apa yang terjadi di balik layar.
Itu biasanya dianggap sebagai hal yang baik, tetapi jelas bergantung pada programmer yang menulis abstraksi yang bermakna. Properti Anda harus berperilaku seperti bidang. Mereka seharusnya tidak memiliki efek samping, mereka tidak boleh melakukan operasi yang mahal atau tidak aman. Kami ingin bisa menganggapnya sebagai ladang.
Namun, saya punya alasan lain untuk menemukan mereka kurang sempurna. Mereka tidak dapat diteruskan dengan mengacu pada fungsi lain.
Bidang dapat dilewatkan sebagai
ref
, memungkinkan fungsi yang dipanggil untuk mengaksesnya secara langsung. Fungsi dapat diberikan sebagai delegasi, memungkinkan fungsi yang dipanggil untuk mengaksesnya secara langsung.Properti ... tidak bisa.
Itu menyebalkan.
Tetapi itu tidak berarti properti itu jahat atau tidak boleh digunakan. Untuk banyak tujuan, mereka hebat.
sumber
ref
params; anggota (misalnyaFoo
) dari formulirvoid Foo<T>(ActionByRef<Point,T> proc, ref T param)
dengan atribut khusus, dan kompilator diubahthing.Foo.X+=someVar;
menjadiFoo((ref Point it, ref int param)=>it.X += param, ref someVar);
. Karena lambda adalah delegasi statis, tidak ada penutupan yang diperlukan, dan pengguna suatu objek akan dapat menggunakan lokasi penyimpanan apa pun yang mendukung properti sebagairef
parameter asli (dan dapat meneruskannya ke metode lain sebagairef
parameter).Kembali pada tahun 2009, saran ini hanya tampak seperti sakit perut dari varietas Who Moved My Cheese . Hari ini, itu hampir usang.
Satu hal yang sangat penting yang tampaknya banyak dijawab tetapi tidak cukup dibahas adalah bahwa "bahaya" properti yang diklaim ini merupakan bagian yang disengaja dari desain kerangka kerja!
Ya, properti dapat:
Tentukan pengubah akses yang berbeda untuk pengambil dan penyetel. Ini adalah keunggulan dibandingkan ladang. Pola yang umum adalah memiliki pengambil publik dan penyetel yang dilindungi atau internal , teknik pewarisan yang sangat berguna yang tidak dapat dicapai oleh bidang saja.
Lempar pengecualian. Sampai saat ini, metode ini tetap menjadi salah satu metode validasi yang paling efektif, terutama saat bekerja dengan framework UI yang melibatkan konsep pengikatan data. Jauh lebih sulit untuk memastikan bahwa suatu objek tetap dalam keadaan valid saat bekerja dengan bidang.
Butuh waktu lama untuk mengeksekusinya. Perbandingan yang valid di sini adalah dengan metode , yang membutuhkan waktu yang sama panjangnya - bukan bidang . Tidak ada dasar yang diberikan untuk pernyataan "metode lebih disukai" selain preferensi pribadi satu penulis.
Kembalikan nilai yang berbeda dari pengambil pada eksekusi selanjutnya. Ini hampir tampak seperti lelucon yang sangat dekat dengan poin yang memuji kebaikan
ref
/out
parameter dengan bidang, yang nilainya bidang setelahref
/out
panggilan cukup banyak dijamin berbeda dari nilai sebelumnya, dan tidak dapat diprediksi.Jika kita berbicara tentang kasus spesifik (dan praktis akademis) akses single-threaded tanpa kopling aferen, cukup dipahami dengan baik bahwa itu hanya desain properti yang buruk untuk memiliki efek samping yang terlihat-berubah, dan mungkin ingatan saya adalah memudar, tetapi saya tidak bisa mengingat contoh orang yang menggunakan
DateTime.Now
dan mengharapkan nilai yang sama muncul setiap saat. Setidaknya tidak ada contoh di mana mereka tidak akan mengacaukannya sama buruknya dengan hipotesisDateTime.Now()
.Menyebabkan efek samping yang dapat diamati - yang tentu saja merupakan alasan mengapa properti diciptakan sebagai fitur bahasa sejak awal. Pedoman Desain Properti Microsoft sendiri menunjukkan bahwa urutan penyetel seharusnya tidak menjadi masalah, karena jika tidak, akan menyiratkan penggandengan sementara . Tentu saja, Anda tidak dapat mencapai penggandengan sementara dengan bidang saja, tetapi itu hanya karena Anda tidak dapat menyebabkan perilaku yang berarti terjadi dengan bidang saja, hingga beberapa metode dijalankan.
Pengakses properti sebenarnya dapat membantu mencegah jenis kopling temporal tertentu dengan memaksa objek ke dalam keadaan valid sebelum tindakan apa pun diambil - misalnya, jika kelas memiliki a
StartDate
danEndDate
, kemudian menyetelEndDate
sebelumStartDate
dapat memaksaStartDate
mundur juga. Ini berlaku bahkan di lingkungan multi-utas atau asinkron, termasuk contoh nyata dari antarmuka pengguna yang digerakkan oleh peristiwa.Hal-hal lain yang dapat dilakukan properti yang tidak dapat disertakan bidang:
Type
atauName
kelas turunan dapat memberikan metadata yang menarik namun tetap konstan tentang dirinya.Item(i)
panggilan yang tak terhindarkan akan mengenali sebagai hal yang luar biasa.Richter jelas seorang penulis yang produktif dan tahu banyak tentang CLR dan C #, tetapi saya harus mengatakan, sepertinya ketika dia pertama kali menulis saran ini (saya tidak yakin apakah itu dalam revisi yang lebih baru - saya sangat berharap tidak) bahwa dia hanya tidak ingin melepaskan kebiasaan lamanya dan mengalami kesulitan menerima konvensi C # (vs. C ++, misalnya).
Yang saya maksud dengan ini adalah, argumen "properti dianggap berbahaya" pada dasarnya bermuara pada satu pernyataan: Properti tampak seperti bidang, tetapi mungkin tidak bertindak seperti bidang. Dan masalah dengan pernyataan itu adalah, itu tidak benar , atau paling-paling itu sangat menyesatkan. Properti tidak terlihat seperti bidang - setidaknya, tidak seharusnya terlihat seperti bidang.
Ada dua konvensi pengkodean yang sangat kuat di C # dengan konvensi serupa yang dimiliki oleh bahasa CLR lainnya, dan FXCop akan berteriak pada Anda jika Anda tidak mengikutinya:
Jadi, tidak ada ambiguitas mengenai apakah
Foo.Bar = 42
itu pengakses properti atau pengakses lapangan. Ini adalah pengakses properti dan harus diperlakukan seperti metode lainnya - mungkin lambat, mungkin akan memunculkan pengecualian, dll. Itulah sifat Abstraksi - sepenuhnya tergantung pada kebijaksanaan kelas yang mendeklarasikan cara bereaksi. Desainer kelas harus menerapkan prinsip paling tidak mengejutkan tetapi penelepon tidak boleh berasumsi apa pun tentang properti kecuali properti tersebut melakukan apa yang tertulis di kemasan. Itu sengaja.Alternatif untuk properti adalah metode pengambil / penyetel di mana-mana. Itulah pendekatan Java, dan sudah kontroversial sejak awal . Tidak apa-apa jika itu tas Anda, tetapi itu bukan cara kami bergabung di kamp .NET. Kami mencoba, setidaknya dalam batasan sistem yang diketik secara statis, untuk menghindari panggilan Fowler Noise Sintaksis . Kami tidak ingin tanda kurung ekstra, ekstra
get
/set
kutil, atau tanda tangan metode tambahan - tidak jika kami dapat menghindarinya tanpa kehilangan kejelasan.Katakan apapun yang kamu suka, tapi
foo.Bar.Baz = quux.Answers[42]
itu akan selalu jauh lebih mudah dibaca daripadafoo.getBar().setBaz(quux.getAnswers().getItem(42))
. Dan saat Anda membaca ribuan baris ini setiap hari, itu membuat perbedaan.(Dan jika tanggapan alami Anda terhadap paragraf di atas adalah mengatakan, "pasti sulit dibaca, tetapi akan lebih mudah jika Anda membaginya menjadi beberapa baris", maka saya minta maaf untuk mengatakan bahwa Anda benar - benar melewatkan intinya .)
sumber
Saya tidak melihat alasan mengapa Anda tidak boleh menggunakan Properti secara umum.
Properti otomatis di C # 3+ hanya menyederhanakan sintaks sedikit (a la syntatic sugar).
sumber
Itu hanya pendapat satu orang. Saya telah membaca beberapa c # buku dan saya belum pernah melihat orang lain mengatakan "jangan gunakan properti".
Menurut saya pribadi, properti adalah salah satu hal terbaik tentang c #. Mereka memungkinkan Anda untuk mengekspos keadaan melalui mekanisme apa pun yang Anda suka. Anda dapat dengan malas memberi contoh saat pertama kali sesuatu digunakan dan Anda dapat melakukan validasi pada pengaturan nilai dll. Saat menggunakan dan menulisnya, saya hanya memikirkan properti sebagai penyetel dan pengambil yang sintaksnya jauh lebih baik.
Adapun keberatan dengan properti, ada pasangan. Salah satunya mungkin adalah penyalahgunaan properti, yang lainnya mungkin tidak kentara.
Pertama, properti adalah jenis metode. Mengejutkan jika Anda menempatkan logika yang rumit dalam sebuah properti karena sebagian besar pengguna kelas akan mengharapkan properti tersebut menjadi cukup ringan.
Misalnya
Saya menemukan bahwa menggunakan metode untuk kasus ini membantu membuat perbedaan.
Kedua, dan yang lebih penting, properti dapat memiliki efek samping jika Anda mengevaluasinya saat melakukan debug.
Katakanlah Anda memiliki beberapa properti seperti ini:
Sekarang misalkan Anda memiliki pengecualian yang terjadi ketika terlalu banyak kueri dibuat. Tebak apa yang terjadi ketika Anda mulai men-debug dan menggulirkan properti di debugger? Hal buruk. Hindari melakukan ini! Melihat properti mengubah status program.
Ini adalah satu-satunya peringatan yang saya miliki. Menurut saya, manfaat properti jauh lebih penting daripada masalahnya.
sumber
Alasan itu pasti diberikan dalam konteks yang sangat spesifik. Biasanya sebaliknya - disarankan untuk menggunakan properti karena properti memberi Anda tingkat abstraksi yang memungkinkan Anda mengubah perilaku kelas tanpa memengaruhi kliennya ...
sumber
Saya tidak bisa tidak memilih detail dari pendapat Jeffrey Richter:
Salah: Bidang dapat ditandai hanya-baca sehingga hanya konstruktor objek yang dapat menulis padanya.
Salah: Implementasi kelas dapat mengubah pengubah akses suatu bidang dari publik menjadi pribadi. Upaya untuk membaca bidang pribadi pada waktu proses akan selalu menghasilkan pengecualian.
sumber
Saya tidak setuju dengan Jeffrey Richter, tapi saya bisa menebak mengapa dia tidak menyukai properti (saya belum membaca bukunya).
Meskipun, properti hanya seperti metode (penerapannya), sebagai pengguna kelas, saya berharap propertinya berperilaku "lebih atau kurang" seperti bidang publik, misalnya:
Sayangnya, saya telah melihat properti yang tidak berperilaku seperti itu. Tapi masalahnya bukan pada properti itu sendiri, tetapi orang yang menerapkannya. Jadi itu hanya membutuhkan pendidikan.
sumber
Ada saat ketika saya mempertimbangkan untuk tidak menggunakan properti, dan itu ada dalam penulisan kode .Net Compact Framework. Kompilator CF JIT tidak melakukan pengoptimalan yang sama seperti kompiler JIT desktop dan tidak mengoptimalkan pengakses properti sederhana, jadi dalam kasus ini menambahkan properti sederhana menyebabkan sejumlah kecil kode membengkak saat menggunakan bidang publik. Biasanya ini tidak akan menjadi masalah, tetapi hampir selalu di dunia Compact Framework Anda menghadapi kendala memori yang ketat, jadi bahkan penghematan kecil seperti ini akan dihitung.
sumber
Anda tidak boleh menghindari penggunaannya tetapi Anda harus menggunakannya dengan kualifikasi dan perhatian, untuk alasan yang diberikan oleh kontributor lain.
Saya pernah melihat properti bernama sesuatu seperti Pelanggan yang secara internal membuka panggilan di luar proses ke database dan membaca daftar pelanggan. Kode klien memiliki 'for (int i to Customers.Count)' yang menyebabkan panggilan terpisah ke database pada setiap iterasi dan untuk akses Pelanggan yang dipilih. Itu adalah contoh mengerikan yang mendemonstrasikan prinsip menjaga properti sangat ringan - jarang lebih dari akses lapangan internal.
Satu argumen UNTUK menggunakan properti adalah bahwa properti memungkinkan Anda untuk memvalidasi nilai yang sedang ditetapkan. Lain adalah bahwa nilai properti dapat berupa nilai turunan, bukan satu bidang, seperti TotalValue = jumlah * kuantitas.
sumber
Secara pribadi saya hanya menggunakan properti saat membuat metode get / set sederhana. Saya menyimpang darinya ketika datang ke struktur data yang rumit.
sumber
Argumen tersebut mengasumsikan bahwa properti itu buruk karena terlihat seperti bidang, tetapi dapat melakukan tindakan yang mengejutkan. Asumsi ini dibatalkan oleh ekspektasi pemrogram .NET:
Properti tidak terlihat seperti bidang. Bidang terlihat seperti properti.
Jadi, sebuah field ibarat properti yang dijamin tidak akan pernah memunculkan eksepsi.
Jadi, bidang itu seperti properti, tetapi memiliki kemampuan tambahan: meneruskan ke metode
ref
/out
menerima.Jadi, ladang ibarat properti cepat.
Jadi, bidang itu seperti properti yang dijamin mengembalikan nilai yang sama kecuali bidang itu disetel ke nilai yang berbeda.
Sekali lagi, bidang adalah properti yang dijamin tidak akan melakukannya.
Yang ini mungkin mengejutkan, tetapi bukan karena properti yang melakukan ini. Sebaliknya, hampir tidak ada yang mengembalikan salinan yang bisa berubah dalam properti, sehingga kasus 0,1% mengejutkan.
sumber
Memanggil metode alih-alih properti sangat mengurangi keterbacaan kode pemanggilan. Di J #, misalnya, menggunakan ADO.NET adalah mimpi buruk karena Java tidak mendukung properti dan pengindeks (yang pada dasarnya adalah properti dengan argumen). Kode yang dihasilkan sangat jelek, dengan panggilan metode tanda kurung kosong di semua tempat.
Dukungan untuk properti dan pengindeks adalah salah satu keunggulan dasar C # dibandingkan Java.
sumber