Apa manfaat untuk menandai bidang sebagai `readonly` dalam C #?

295

Apa manfaat memiliki variabel anggota yang dinyatakan sebagai hanya baca? Apakah itu hanya melindungi seseorang yang mengubah nilainya selama siklus hidup kelas atau apakah menggunakan kata kunci ini menghasilkan peningkatan kecepatan atau efisiensi?

leora
sumber
8
Jawaban eksternal yang bagus: dotnetperls.com/readonly
OneWorld
3
Menarik. Ini pada dasarnya setara dengan C # untuk pertanyaan Java ini stackoverflow.com/questions/137868/... Diskusi di sini jauh lebih sedikit panas ... hmm ...
RAY
7
Mungkin perlu dicatat bahwa readonlybidang tipe struktur memaksakan penalti kinerja dibandingkan dengan bidang yang bisa berubah yang sama sekali tidak bermutasi, karena pemanggilan anggota mana pun dari readonlybidang tipe nilai akan menyebabkan kompilator membuat salinan bidang dan meminta anggota itu.
supercat
2
lebih lanjut tentang penalti kinerja: codeblog.jonskeet.uk/2014/07/16/...
CAD bloke

Jawaban:

171

Kata readonlykunci digunakan untuk mendeklarasikan variabel anggota sebagai konstanta, tetapi memungkinkan nilai tersebut dihitung saat runtime. Ini berbeda dari konstanta yang dideklarasikan dengan constpengubah, yang harus ditetapkan nilainya pada waktu kompilasi. Menggunakan readonlyAnda dapat mengatur nilai bidang baik dalam deklarasi, atau dalam konstruktor objek yang bidangnya anggota.

Juga gunakan itu jika Anda tidak ingin harus mengkompilasi ulang DLL eksternal yang mereferensikan konstanta (karena akan diganti pada waktu kompilasi).

Bill the Lizard
sumber
193

Saya tidak percaya ada keuntungan kinerja dari menggunakan bidang baca saja. Ini hanya pemeriksaan untuk memastikan bahwa setelah objek dibangun sepenuhnya, bidang itu tidak dapat diarahkan ke nilai baru.

Namun "readonly" sangat berbeda dari jenis semantik read-only lainnya karena diberlakukan saat runtime oleh CLR. Kata kunci yang hanya dibaca dikompilasi hingga .initonly yang dapat diverifikasi oleh CLR.

Keuntungan sebenarnya dari kata kunci ini adalah menghasilkan struktur data yang tidak dapat diubah. Struktur data yang tidak dapat berubah menurut definisi tidak dapat diubah begitu dibangun. Ini membuatnya sangat mudah untuk berpikir tentang perilaku struktur saat runtime. Misalnya, tidak ada bahaya menularkan struktur yang tidak dapat diubah ke bagian kode acak lainnya. Mereka tidak dapat mengubahnya sehingga Anda dapat memprogram dengan andal terhadap struktur itu.

Berikut adalah entri yang bagus tentang salah satu manfaat imutabilitas: Threading

JaredPar
sumber
6
Jika Anda membaca ini, stackoverflow.com/questions/9860595/... baca saja anggota dapat dimodifikasi dan terlihat seperti perilaku tidak konsisten oleh .net
Akash Kava
Bisakah Anda memperbarui tautan ke pos di Threading di atas? Itu rusak.
Akshay Khot
69

Tidak ada manfaat kinerja yang jelas untuk digunakan readonly, setidaknya tidak ada yang pernah saya lihat disebutkan di mana saja. Itu hanya untuk melakukan persis seperti yang Anda sarankan, untuk mencegah modifikasi setelah diinisialisasi.

Jadi ini bermanfaat karena membantu Anda menulis kode yang lebih kuat, lebih mudah dibaca. Manfaat nyata dari hal-hal seperti ini datang ketika Anda bekerja dalam tim atau untuk pemeliharaan. Mendeklarasikan sesuatu seperti readonlymeletakkan kontrak untuk penggunaan variabel itu dalam kode. Anggap saja sebagai menambahkan dokumentasi dengan cara yang sama seperti kata kunci lain suka internalatau private, Anda mengatakan "variabel ini tidak boleh dimodifikasi setelah inisialisasi", dan terlebih lagi Anda menegakkannya .

Jadi, jika Anda membuat kelas dan menandai beberapa variabel anggota readonlydengan desain, maka Anda mencegah diri sendiri atau anggota tim lainnya membuat kesalahan nanti ketika mereka memperluas atau memodifikasi kelas Anda. Menurut pendapat saya, itu adalah manfaat yang berharga (dengan sedikit biaya kompleksitas bahasa tambahan seperti yang disebutkan oleh doofledorfer dalam komentar).

Xiaofu
sumber
Dan otoh mendeskripsikan bahasa. Namun, tidak menyangkal pernyataan manfaat Anda.
dkretz
3
Saya setuju, tapi saya kira manfaat sebenarnya datang ketika lebih dari satu orang mengerjakan kode. Ini seperti memiliki pernyataan desain kecil dalam kode, kontrak untuk penggunaannya. Saya mungkin harus menjawabnya, hehe.
Xiaofu
7
Jawaban dan diskusi ini sebenarnya adalah jawaban terbaik menurut pendapat saya +1
Jeff Martin
@Xiaofu: Anda membuat saya konstan terhadap ide hahaha readonly penjelasan yang indah bahwa tidak ada seorang pun di dunia ini yang bisa menjelaskan pikiran paling konyol yang dipahami
Learner
Yaitu Anda tetap niat dalam kode Anda bahwa nilai ini tidak boleh berubah setiap saat.
Andez
51

Singkatnya:

Jika Anda menggunakan const di dll A dan dll B referensi const itu, nilai const itu akan dikompilasi menjadi dll B. Jika Anda memindahkan dll A dengan nilai baru untuk const itu, dll B masih akan menggunakan nilai asli.

Jika Anda menggunakan readonly di referensi A dan dll B dll yang hanya baca, readonly itu akan selalu dilihat saat runtime. Ini berarti jika Anda memindahkan dll A dengan nilai baru untuk readonly itu, dll B akan menggunakan nilai baru itu.

Daniel Auger
sumber
4
Ini adalah contoh praktis yang baik untuk memahami perbedaannya. Terima kasih.
Shyju
Di sisi lain, constmungkin ada peningkatan kinerja readonly. Berikut ini penjelasan yang lebih dalam dengan kode: dotnetperls.com/readonly
Dio Phung
2
Saya pikir istilah praktis terbesar hilang dari jawaban ini: kemampuan untuk menyimpan nilai yang dihitung saat runtime ke dalam readonlybidang. Anda tidak dapat menyimpan new object();dalam constdan itu masuk akal karena Anda tidak dapat memanggang hal-hal yang tidak bernilai seperti referensi ke majelis lain selama waktu kompilasi tanpa mengubah identitas.
binki
14

Ada kasus potensial di mana kompiler dapat membuat pengoptimalan kinerja berdasarkan pada kehadiran kata kunci yang hanya dibaca.

Ini hanya berlaku jika bidang hanya baca juga ditandai sebagai statis . Dalam hal ini, kompiler JIT dapat mengasumsikan bahwa bidang statis ini tidak akan pernah berubah. Kompiler JIT dapat mempertimbangkan ini saat mengkompilasi metode kelas.

Contoh umum: kelas Anda bisa memiliki bidang IsDebugLoggingEnabled baca statis yang diinisialisasi dalam konstruktor (misalnya berdasarkan pada file konfigurasi). Setelah metode aktual dikompilasi JIT, kompiler dapat menghilangkan seluruh bagian kode ketika debug logging tidak diaktifkan.

Saya belum memeriksa apakah optimasi ini benar-benar diimplementasikan dalam versi JIT compiler saat ini, jadi ini hanya spekulasi.

Kristof Verbiest
sumber
apakah ada sumber untuk ini?
Sedat Kapanoglu
4
Kompiler JIT saat ini sebenarnya mengimplementasikan ini, dan telah sejak CLR 3.5. github.com/dotnet/coreclr/issues/1079
mirhagk
Tidak ada optimisasi yang dapat dilakukan pada bidang hanya baca karena alasan sederhana bahwa bidang hanya baca tidak hanya dibaca tetapi dibaca. Mereka hanya petunjuk kompiler yang menghormati sebagian besar kompiler dan nilai bidang baca hanya dapat dengan mudah ditimpa melalui refleksi (meskipun tidak dalam kode yang dipercaya sebagian).
Christoph
9

Perlu diingat bahwa readonly hanya berlaku untuk nilai itu sendiri, jadi jika Anda menggunakan tipe referensi readonly hanya melindungi referensi dari perubahan. Keadaan instance tidak dilindungi oleh readonly.

Brian Rasmussen
sumber
4

Jangan lupa ada solusi untuk mendapatkan readonlybidang yang ditetapkan di luar konstruktor menggunakan outparams.

Agak berantakan tapi:

private readonly int _someNumber;
private readonly string _someText;

public MyClass(int someNumber) : this(data, null)
{ }

public MyClass(int someNumber, string someText)
{
    Initialise(out _someNumber, someNumber, out _someText, someText);
}

private void Initialise(out int _someNumber, int someNumber, out string _someText, string someText)
{
    //some logic
}

Diskusi lebih lanjut di sini: http://www.adamjamesnaylor.com/2013/01/23/Setting-Readonly-Fields-From-Chained-Constructors.aspx

Adam Naylor
sumber
4
Kolom masih ditugaskan di konstruktor .. tidak ada "menyiasati" itu. Tidak masalah jika nilai berasal dari ekspresi tunggal, dari tipe kompleks terurai, atau ditugaskan melalui panggilan dengan semantik referensi dari out..
user2864740
Ini bahkan tidak berusaha menjawab pertanyaan.
Sheridan
2

Anehnya, readonly sebenarnya dapat menghasilkan kode yang lebih lambat, seperti yang ditemukan Jon Skeet ketika menguji pustaka Noda Time-nya. Dalam kasus ini, tes yang berjalan dalam 20 detik hanya membutuhkan waktu 4 detik setelah menghapus hanya baca.

https://codeblog.jonskeet.uk/2014/07/16/micro-optimization-the-surprising-inefficiency-of-readonly-fields/

Neil
sumber
3
Perhatikan bahwa jika bidang adalah tipe yang ada readonly structdi C # 7.2, manfaat membuat bidang tidak dibaca hanya hilang.
Jon Skeet
1

Jika Anda memiliki nilai yang ditentukan sebelumnya atau yang dihitung sebelumnya yang perlu tetap sama sepanjang program maka Anda harus menggunakan konstanta tetapi jika Anda memiliki nilai yang perlu diberikan pada saat runtime tetapi sekali ditugaskan harus tetap sama di seluruh program Anda harus menggunakan dibaca saja. misalnya jika Anda harus menetapkan waktu mulai program atau Anda harus menyimpan nilai yang disediakan pengguna pada inisialisasi objek dan Anda harus membatasi itu dari perubahan lebih lanjut yang harus Anda gunakan hanya baca.

waqar ahmed
sumber
1

Menambahkan aspek dasar untuk menjawab pertanyaan ini:

Properti dapat diekspresikan sebagai hanya dibaca dengan meninggalkan setoperator. Jadi dalam kebanyakan kasus, Anda tidak perlu menambahkan readonlykata kunci ke properti:

public int Foo { get; }  // a readonly property

Berbeda dengan itu: Bidang membutuhkan readonlykata kunci untuk mencapai efek yang serupa:

public readonly int Foo; // a readonly field

Jadi, satu manfaat dari menandai sebuah lapangan readonlyadalah untuk mencapai tingkat perlindungan penulisan yang sama seperti properti tanpa setoperator - tanpa harus mengubah lapangan ke properti, jika karena alasan apa pun, yang diinginkan.

kode14214
sumber
Apakah ada perbedaan perilaku antara keduanya?
petrosmm
0

Hati-hati dengan susunan baca baca pribadi. Jika ini mengekspos klien sebagai objek (Anda mungkin melakukan ini untuk COM interop seperti yang saya lakukan) klien dapat memanipulasi nilai array. Gunakan metode Clone () saat mengembalikan array sebagai objek.

Michael
sumber
20
Tidak; mengekspos ReadOnlyCollection<T>bukan array.
SLaks
Ini harus menjadi komentar bukan jawaban karena tidak memberikan jawaban untuk pertanyaan ...
Peter
Lucunya, saya disuruh meletakkan hal semacam ini sebagai jawaban, bukan komentar ketika saya melakukannya di posting lain minggu lalu.
Kyle Baran
Pada 2013, Anda dapat menggunakan ImmutableArray<T>, yang menghindari tinju ke antarmuka ( IReadOnlyList<T>) atau membungkus sebuah kelas ( ReadOnlyCollection). Ini memiliki kinerja yang sebanding dengan array asli: blogs.msdn.microsoft.com/dotnet/2013/06/24/…
Charles Taylor
0

Mungkin ada manfaat kinerja di WPF, karena menghilangkan kebutuhan akan DependencyProperties yang mahal. Ini bisa sangat berguna dengan koleksi

Shane
sumber
0

Bagian lain yang menarik dari penggunaan tanda baca hanya dapat melindungi bidang dari inisialisasi dalam singleton.

misalnya dalam kode dari csharpindepth :

public sealed class Singleton
{
    private static readonly Lazy<Singleton> lazy =
        new Lazy<Singleton>(() => new Singleton());

    public static Singleton Instance { get { return lazy.Value; } }

    private Singleton()
    {
    }
}

readonly memainkan peran kecil melindungi bidang Singleton dari diinisialisasi dua kali. Detail lain adalah bahwa untuk skenario yang disebutkan Anda tidak dapat menggunakan const karena const memaksa penciptaan selama waktu kompilasi, tetapi singleton membuat penciptaan pada saat run time.

Yuriy Zaletskyy
sumber
0

readonlydapat diinisialisasi pada saat deklarasi atau mendapatkan nilainya dari konstruktor saja. Berbeda dengan constitu harus diinisialisasi dan menyatakan pada saat yang sama. readonly memiliki semuanya const , ditambah inisialisasi konstruktor

kode https://repl.it/HvRU/1

using System;

class MainClass {
    public static void Main (string[] args) {

        Console.WriteLine(new Test().c);
        Console.WriteLine(new Test("Constructor").c);
        Console.WriteLine(new Test().ChangeC()); //Error A readonly field 
        // `MainClass.Test.c' cannot be assigned to (except in a constructor or a 
        // variable initializer)
    }


    public class Test {
        public readonly string c = "Hello World";
        public Test() {

        }

        public Test(string val) {
          c = val;
        }

        public string ChangeC() {
            c = "Method";
            return c ;
        }
    }
}
Mina Gabriel
sumber