Mengapa parameter const tidak diperbolehkan di C #?

102

Ini terlihat aneh terutama untuk pengembang C ++. Di C ++ kami biasa menandai parameter sebagai constuntuk memastikan bahwa statusnya tidak akan diubah dalam metode. Ada juga alasan spesifik C ++ lainnya, seperti passing const refuntuk melewati ref dan memastikan bahwa status tidak akan berubah. Tapi mengapa kita tidak bisa menandai sebagai parameter metode const di C #?

Mengapa saya tidak dapat mendeklarasikan metode saya seperti berikut?

    ....
    static void TestMethod1(const MyClass val)
    {}
    ....
    static void TestMethod2(const int val)
    {}
    ....
Penyamaran
sumber
Itu selalu hanya fitur pengaman di C ++, dan tidak pernah benar-benar integral dengan bahasa seperti yang saya lihat. C # devs tidak berpikir itu menambah banyak nilai secara nyata.
Noldorin
3
Diskusi yang lebih besar pada pertanyaan ini: "Const-correctness in c #": stackoverflow.com/questions/114149/const-correctness-in-c
tenpn
Ada untuk tipe nilai [out / ref], tapi bukan tipe referensi. Itu pertanyaan yang menarik.
kenny
4
Bagaimana mungkin pertanyaan ini memiliki tag C # dan bahasa-agnostik?
CJ7
1
Data dan fungsi yang tidak dapat diubah tanpa efek samping lebih mudah untuk dipikirkan. Anda dapat menikmati F # fsharpforfunandprofit.com/posts/is-your-language-unreasonable
Colonel Panic

Jawaban:

59

Sebagai tambahan dari jawaban bagus lainnya, saya akan menambahkan alasan lain mengapa untuk tidak menempatkan kekonstanan gaya-C ke dalam C #. Kamu berkata:

kami menandai parameter sebagai const untuk memastikan bahwa statusnya tidak akan diubah dalam metode.

Jika const benar-benar melakukan itu, itu akan bagus. Const tidak melakukan itu. Const adalah bohong!

Const tidak memberikan jaminan apa pun yang benar-benar dapat saya gunakan. Misalkan Anda memiliki metode yang mengambil benda const. Ada dua penulis kode: orang yang menulis penelepon dan orang yang menulis panggilan . Penulis callee telah membuat metode take a const. Apa yang dapat diasumsikan oleh kedua penulis itu tidak tetap tentang objek?

Tidak ada. Callee bebas untuk membuang const dan memutasi objek, jadi pemanggil tidak memiliki jaminan bahwa memanggil metode yang menggunakan const sebenarnya tidak akan memutasinya. Demikian pula, callee tidak dapat berasumsi bahwa konten objek tidak akan berubah selama tindakan callee; callee bisa memanggil beberapa metode mutasi pada alias non const dari objek const, dan sekarang yang disebut objek const telah berubah .

C-style const tidak memberikan jaminan bahwa objek tidak akan berubah, dan karenanya rusak. Sekarang, C sudah memiliki sistem tipe lemah di mana Anda dapat melakukan interpretasi ulang pemeran ganda menjadi int jika Anda benar-benar menginginkannya, jadi tidak mengherankan bahwa ia memiliki sistem tipe yang lemah sehubungan dengan const juga. Tapi C # dirancang untuk memiliki sistem tipe yang baik , sistem tipe di mana ketika Anda mengatakan "variabel ini berisi string" bahwa variabel sebenarnya berisi referensi ke string (atau null). Kami benar-benar tidak ingin menempatkan pengubah "const" gaya-C ke dalam sistem tipe karena kami tidak ingin sistem tipe menjadi kebohongan . Kami ingin sistem tipe kuat sehingga Anda dapat bernalar dengan benar tentang kode Anda.

Const dalam C adalah pedoman ; itu pada dasarnya berarti "Anda dapat mempercayai saya untuk tidak mencoba mengubah hal ini". Itu seharusnya tidak dalam sistem tipe ; hal-hal dalam sistem tipe haruslah fakta tentang objek yang bisa Anda pertimbangkan, bukan pedoman penggunaannya.

Sekarang, jangan salah paham; hanya karena const di C sangat rusak tidak berarti bahwa seluruh konsep tidak berguna. Yang ingin saya lihat adalah beberapa bentuk anotasi "const" yang benar- benar benar dan berguna di C #, anotasi yang dapat digunakan oleh manusia dan penyusun untuk membantu mereka memahami kode, dan yang dapat digunakan runtime untuk melakukan hal-hal seperti kelumpuhan otomatis dan pengoptimalan lanjutan lainnya.

Misalnya, bayangkan jika Anda bisa "menggambar kotak" di sekitar sebongkah kode dan berkata "Saya jamin kumpulan kode ini tidak melakukan mutasi ke bidang mana pun dari kelas ini" dengan cara yang dapat diperiksa oleh penyusun. Atau gambar kotak yang mengatakan " metode murni ini mengubah keadaan internal objek tetapi tidak dengan cara apa pun yang dapat diamati di luar kotak". Objek seperti itu tidak dapat dengan aman di-multi-threaded secara otomatis tetapi dapat secara otomatis dibuat memo . Ada berbagai macam anotasi menarik yang dapat kami masukkan ke dalam kode yang memungkinkan pengoptimalan yang kaya dan pemahaman yang lebih dalam. Kita bisa melakukan cara yang lebih baik daripada anotasi const gaya-C yang lemah.

Namun, saya tegaskan bahwa ini hanyalah spekulasi . Kami tidak memiliki rencana pasti untuk menempatkan fitur semacam ini ke dalam versi hipotetis C # di masa depan, jika ada, yang belum kami umumkan dengan satu atau lain cara. Ini adalah sesuatu yang saya ingin lihat, dan sesuatu yang mungkin diperlukan oleh penekanan pada komputasi multi-core, tetapi tidak satupun dari ini harus ditafsirkan sebagai prediksi atau jaminan fitur tertentu atau arah masa depan untuk C #.

Sekarang, jika yang Anda inginkan hanyalah anotasi pada variabel lokal yang merupakan parameter yang mengatakan "nilai parameter ini tidak berubah sepanjang metode", maka, tentu, itu akan mudah dilakukan. Kami dapat mendukung lokal "readonly" dan parameter yang akan diinisialisasi satu kali, dan kesalahan waktu kompilasi untuk mengubah metode. Variabel yang dideklarasikan oleh pernyataan "using" sudah seperti itu; kita bisa menambahkan anotasi opsional ke semua lokal dan parameter untuk membuatnya bertindak seperti variabel "menggunakan". Ini tidak pernah menjadi fitur dengan prioritas yang sangat tinggi sehingga tidak pernah diterapkan.

Eric Lippert
sumber
34
Maaf, tetapi menurut saya argumen ini sangat lemah, fakta bahwa pengembang dapat mengatakan kebohongan tidak dapat dicegah dalam bahasa apa pun. void foo (const A & a) yang memodifikasi objek a tidak berbeda dari int countApples (Basket b) yang mengembalikan jumlah pir. Itu hanya bug, bug yang terkompilasi dalam bahasa apapun.
Alessandro Teruzzi
55
“Callee bebas untuk membuang const…” uhhhhh… (° _ °) Ini adalah argumen yang cukup dangkal yang Anda buat di sini. Callee juga bebas untuk mulai menulis lokasi memori acak dengan 0xDEADBEEF. Tetapi keduanya akan menjadi pemrograman yang sangat buruk , dan tertangkap awal dan secara pedih oleh kompiler modern manapun. Di masa mendatang saat menulis jawaban, harap jangan bingung antara apa yang secara teknis mungkin dengan menggunakan pintu belakang bahasa dengan apa yang layak dalam kode dunia nyata yang sebenarnya. Informasi miring seperti ini membingungkan pembaca yang kurang berpengalaman, dan menurunkan kualitas info di SO.
Slipp D. Thompson
8
"Const di C adalah pedoman;" Mengutip sumber untuk beberapa hal yang Anda katakan akan sangat membantu kasus Anda di sini. Dalam pengalaman pendidikan dan industri saya, pernyataan yang dikutip adalah omong kosong — const di C adalah fitur bawaan wajib sebanyak sistem tipe itu sendiri — keduanya memiliki pintu belakang untuk menipu mereka saat diperlukan (seperti semua yang ada di C) tetapi diharapkan untuk digunakan oleh buku dalam 99,99% kode.
Slipp D. Thompson
29
Jawaban ini adalah alasan mengapa saya terkadang membenci desain C #. Perancang bahasa benar-benar yakin bahwa mereka jauh lebih pintar daripada pengembang lain dan mencoba melindungi mereka secara berlebihan dari bug. Jelas bahwa kata kunci const hanya bekerja dalam waktu kompilasi, karena di C ++ kita memiliki akses langsung ke memori dan dapat melakukan apa pun yang kita inginkan dan memiliki sejumlah cara untuk mendapatkan deklarasi konstanta bulat. Tetapi tidak ada yang melakukan ini dalam situasi normal. Omong-omong, 'private' dan 'protected' di C # juga tidak menjamin, karena kita bisa mengakses metode atau field ini menggunakan refleksi.
BlackOverlord
13
@BlackOverlord: (1) Keinginan untuk merancang bahasa yang propertinya secara alami mengarahkan pengembang menuju kesuksesan daripada kegagalan dimotivasi oleh keinginan untuk membangun alat yang berguna , bukan oleh keyakinan bahwa perancang lebih pintar daripada pelanggan mereka, seperti yang Anda duga. (2) Refleksi bukanlah bagian dari bahasa C #; itu bagian dari runtime; akses ke refleksi dibatasi oleh sistem keamanan runtime. Kode kepercayaan rendah tidak diberikan kemampuan untuk melakukan refleksi pribadi. The jaminan yang disediakan oleh pengubah akses adalah untuk kode kepercayaan yang rendah ; kode kepercayaan tinggi dapat melakukan apa saja.
Eric Lippert
24

Salah satu alasan mengapa tidak ada ketepatan const di C # adalah karena tidak ada di tingkat runtime. Ingatlah bahwa C # 1.0 tidak memiliki fitur apapun kecuali itu adalah bagian dari runtime.

Dan beberapa alasan mengapa CLR tidak memiliki pengertian tentang kebenaran const adalah sebagai contoh:

  1. Ini memperumit runtime; Selain itu, JVM juga tidak memilikinya, dan CLR pada dasarnya dimulai sebagai proyek untuk membuat runtime seperti JVM, bukan runtime seperti C ++.
  2. Jika ada kebenaran const di tingkat runtime, harus ada kebenaran const di BCL, jika tidak, fitur tersebut tidak ada gunanya sejauh menyangkut .NET Framework.
  3. Tetapi jika BCL membutuhkan ketepatan const, setiap bahasa di atas CLR harus mendukung ketepatan const (VB, JavaScript, Python, Ruby, F #, dll.) Itu tidak akan terjadi.

Kebenaran konstan adalah fitur bahasa yang hanya ada di C ++. Jadi intinya adalah argumen yang sama tentang mengapa CLR tidak memerlukan pengecualian yang dicentang (yang merupakan fitur khusus bahasa Java).

Selain itu, saya rasa Anda tidak dapat memperkenalkan fitur sistem tipe fundamental dalam lingkungan terkelola tanpa merusak kompatibilitas ke belakang. Jadi jangan mengandalkan kebenaran const yang pernah memasuki dunia C #.

Ruben
sumber
Apakah Anda yakin bahwa JVM tidak memilikinya. Seperti yang saya tahu, dia memilikinya. Anda dapat mencoba TestMethod public static void (final int val) di Java.
Penyamaran
2
Final bukanlah konstanta. Anda masih dapat mengubah bidang pada IIRC referensi, hanya saja tidak nilai / referensi itu sendiri. (Yang tetap diteruskan oleh nilai, jadi ini lebih merupakan trik kompilator untuk mencegah Anda mengubah nilai parameter.)
Ruben
1
poin ketiga Anda hanya sebagian yang benar. memiliki parameter const untuk panggilan BCL tidak akan menjadi perubahan besar karena mereka hanya menjelaskan kontrak dengan lebih tepat. "Metode ini tidak akan mengubah status objek" berbeda dengan "Metode ini mengharuskan Anda mengirimkan nilai const" yang akan melanggar
Rune FS
2
Itu diberlakukan di dalam metode sehingga tidak akan menjadi perubahan besar untuk memperkenalkannya di BCL.
Rune FS
1
Bahkan jika run-time tidak dapat memberlakukannya, akan sangat membantu jika memiliki atribut yang menentukan apakah suatu fungsi harus diizinkan / diharapkan untuk menulis ke refparameter (terutama, dalam kasus metode / properti struct, this). Jika sebuah metode secara khusus menyatakan bahwa ia tidak akan menulis ke refparameter, maka kompilator harus mengizinkan nilai hanya-baca untuk diteruskan. Sebaliknya, jika suatu metode menyatakan akan menulis this, kompilator tidak boleh mengizinkan metode seperti itu dipanggil pada nilai hanya-baca.
supercat
12

Saya yakin ada dua alasan mengapa C # tidak benar.

Yang pertama adalah pemahaman . Beberapa programmer C ++ memahami const-correctness. Contoh sederhananya const int argadalah lucu, tetapi saya juga melihat char * const * const arg- penunjuk konstan ke petunjuk konstan ke karakter non-konstan. Ketepatan konstan pada pointer ke fungsi adalah tingkat kebingungan yang sama sekali baru.

Yang kedua adalah karena argumen kelas adalah referensi yang diteruskan oleh nilai. Ini berarti sudah ada dua tingkat keteguhan yang harus ditangani, tanpa sintaks yang jelas . Titik sandungan serupa adalah koleksi (dan koleksi koleksi, dll).

Const-correctness adalah bagian penting dari sistem tipe C ++. Itu bisa - secara teori - ditambahkan ke C # sebagai sesuatu yang hanya diperiksa pada waktu kompilasi (itu tidak perlu ditambahkan ke CLR, dan tidak akan mempengaruhi BCL kecuali gagasan metode anggota const dimasukkan) .

Namun, saya percaya ini tidak mungkin: alasan kedua (sintaks) akan cukup sulit dipecahkan, yang akan membuat alasan pertama (pemahaman) menjadi lebih dari masalah.

Stephen Cleary
sumber
1
Anda benar bahwa CLR tidak perlu mengkhawatirkan hal ini, karena seseorang dapat menggunakan modoptdan modreqpada tingkat metadata untuk menyimpan info tersebut (seperti yang sudah dilakukan C + / CLI - lihat System.Runtime.CompilerServices.IsConst); dan ini bisa juga diperluas ke metode const.
Pavel Minaev
Ini bermanfaat tetapi perlu dilakukan dengan cara yang aman. Konstanta Deep / Shallow yang Anda sebutkan membuka seluruh kaleng worm.
pengguna1496062
Dalam c ++ di mana referensi sebenarnya dimanipulasi, saya dapat melihat argumen Anda tentang memiliki dua jenis const. Namun, dalam C # (di mana Anda harus melalui pintu belakang untuk menggunakan "pointer") tampaknya cukup jelas bagi saya bahwa ada makna yang terdefinisi dengan baik untuk jenis referensi (yaitu kelas) dan definisi yang jelas meskipun makna yang berbeda untuk nilai jenis (yaitu primitif, struct)
Assimilater
2
Pemrogram yang tidak dapat memahami konstanta seharusnya tidak memprogram dalam C ++.
Steve White
5

constberarti "konstanta waktu kompilasi" di C #, bukan "hanya-baca tetapi mungkin bisa berubah oleh kode lain" seperti di C ++. Analog kasar dari C ++ constdi C # adalah readonly, tetapi yang satu itu hanya berlaku untuk bidang. Selain itu, tidak ada gagasan tentang kebenaran konstanta seperti C ++ di C # sama sekali.

Alasannya sulit untuk dipastikan, karena ada banyak alasan potensial, tetapi saya kira saya ingin menjaga bahasanya tetap sederhana terlebih dahulu dan terpenting, dan keuntungan yang tidak pasti dari penerapan yang kedua ini.

Pavel Minaev
sumber
Jadi menurut Anda MS menganggap seperti 'noise' memiliki parameter const dalam metode.
Incognito
1
Saya tidak yakin apa itu "kebisingan" - saya lebih suka menyebutnya "rasio biaya / manfaat tinggi". Tapi, tentu saja, Anda membutuhkan seseorang dari tim C # untuk memberi tahu Anda dengan pasti.
Pavel Minaev
Jadi seperti yang saya pahami, argumen Anda adalah bahwa keteguhan ditinggalkan dari C # agar tetap sederhana. Berpikir tentang itu.
Steve White
2

Ini dimungkinkan sejak C # 7.2 (Desember 2017 dengan Visual Studio 2017 15.5).

Hanya untuk Struct dan tipe dasar ! , bukan untuk anggota kelas.

Anda harus menggunakan inuntuk mengirim argumen sebagai masukan dengan referensi. Lihat:

Lihat: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/in-parameter-modifier

Sebagai contoh Anda:

....
static void TestMethod1(in MyStruct val)
{
    val = new MyStruct;  // Error CS8331 Cannot assign to variable 'in MyStruct' because it is a readonly variable
    val.member = 0;  // Error CS8332 Cannot assign to a member of variable 'MyStruct' because it is a readonly variable
}
....
static void TestMethod2(in int val)
{
    val = 0;  // Error CS8331 Cannot assign to variable 'in int' because it is a readonly variable
}
....
hilang
sumber