Чому параметри const не дозволені в C #?


102

Це виглядає дивно, особливо для розробників C ++. У C ++ ми позначали параметр як constдля того, щоб бути впевненим, що його стан не буде змінено у методі. Існують також інші специфічні причини C ++, наприклад, проходження const ref, щоб пройти посилання і бути впевненим, що стан не буде змінено. Але чому ми не можемо позначити як параметр методу const у C #?

Чому я не можу оголосити свій метод таким чином?

    ....
    static void TestMethod1(const MyClass val)
    {}
    ....
    static void TestMethod2(const int val)
    {}
    ....

Це завжди було лише функцією безпеки на C ++, і ніколи не є цілісною для мови, як я бачив. C # devs не думав, що це очевидно додало великої вартості.
Нолдорін

3
Більший обговорення на це питання: «Уст-коректність в C #»: stackoverflow.com/questions/114149/const-correctness-in-c
tenpn

Існує для типів значень [out / ref], але не посилальних типів. Це цікаве питання.
kenny

4
Як це питання може мати як теги C #, так і мови-агностики?
CJ7

1
Про неспроможні дані та функції без побічних ефектів простіше міркувати. Вам може сподобатися F # fsharpforfunandprofit.com/posts/is-your-language-unreasonable
Полковник Паніка

Відповіді:


59

На додаток до інших хороших відповідей, я додам ще одну причину, чому не можна вводити в C # стильність у C #. Ти сказав:

ми позначаємо параметр const, щоб бути впевненим, що його стан не буде змінено у методі.

Якби фактично це робив Конст, це було б чудово. Конст цього не робить. Конст - брехня!

Const не дає жодної гарантії, яку я фактично можу використовувати. Припустимо, у вас є метод, який вимагає рівноваги. Є два автори коду: людина, яка пише абонента, і особа, яка пише абонента . Автор виклику змусив метод прийняти const. Що можна вважати двома авторами щодо об'єкта?

Нічого. Оголошення вільне відкинути const та мутувати об’єкт, тому абонент не має гарантії, що виклик методу, який приймає const, насправді не буде його мутувати. Аналогічно, замовник не може припустити, що вміст об'єкта не змінюватиметься протягом всієї дії виклику; callee може викликати якийсь мутуючий метод на non-const псевдонімі const об'єкта, і тепер так званий об'єкт const змінився .

Const у стилі C не дає гарантії, що об'єкт не зміниться і тому буде зламаний. Тепер, у C вже є система слабкого типу, в якій ви можете зробити реінтерпретовану передачу подвійних в int, якщо цього дуже хочете, тому не повинно бути сюрпризом, що у неї є система слабкого типу і щодо const. Але C # був розроблений для того, щоб мати гарну систему типів, систему типів, де, коли ви говорите "ця змінна містить рядок", що змінна насправді містить посилання на рядок (або null). Ми абсолютно не хочемо вводити модифікатор типу "const" у тип типу в систему типів, оскільки не хочемо, щоб система типів була брехнею . Ми хочемо, щоб система типів була міцною, щоб ви могли правильно міркувати про свій код.

Const в C - це орієнтир ; це в основному означає "ви можете мені довіряти, щоб не намагатися мутувати цю річ". Це не повинно бути в системі типів ; речі в системі типів повинні бути фактом про об'єкт, про який ви можете міркувати, а не керівництвом щодо його використання.

Тепер не зрозумійте мене неправильно; тільки те, що const в C глибоко порушений, не означає, що вся концепція марна. Що я хотів би побачити - це фактично правильна і корисна форма анотації "const" в C #, примітка, яку можуть використовувати і люди, і компілятори, щоб допомогти їм зрозуміти код, і що час виконання може використовуватись для таких дій, як автоматична паралелізація та інші розширені оптимізації.

Наприклад, уявіть, якби ви могли «намалювати поле» навколо лука коду і сказати «Я гарантую, що ця команда коду не виконує жодних мутацій до будь-якого поля цього класу» таким чином, щоб це міг перевірити компілятор. Або намалюйте поле, в якому сказано, що "цей чистий метод мутує внутрішній стан об'єкта, але ніяк не спостерігається поза коробкою". Такий об'єкт не може бути безпечно багатопотоковим автоматично, але він може автоматично запам'ятовуватися . Існують всілякі цікаві примітки, які ми можемо застосувати до коду, які дозволять отримати багаті оптимізації та глибше розуміння. Ми можемо зробити набагато краще, ніж слабкі анотації в стилі С.

Однак наголошую, що це лише спекуляція . Ми не маємо твердих планів включити подібну особливість у будь-яку гіпотетичну майбутню версію C #, якщо вона є навіть такою, про яку ми не оголосили так чи інакше. Мені б хотілося це побачити, і те, на що може знадобитися подальший акцент на багатоядерних обчисленнях, але жодне з цього не повинно розглядатися як передбачення або гарантія будь-якої особливості чи майбутнього напрямку для C #.

Тепер, якщо ви хочете - це лише анотація до локальної змінної, яка є параметром, який говорить, що "значення цього параметра не змінюється протягом усього методу", то, звичайно, це було б легко зробити. Ми можемо підтримувати локальні локальні пристрої та параметри, які були б ініціалізовані один раз, і помилка часу компіляції, щоб змінити метод. Змінна, оголошена оператором "using", вже така локальна; ми можемо додати необов’язкове примітку до всіх локальних пристроїв та параметрів, щоб змусити їх діяти як "використання" змінних. Він ніколи не був дуже пріоритетним, тому він ніколи не був реалізований.


34
Вибачте, але я вважаю цей аргумент дуже слабким, той факт, що розробник може сказати брехню, не може бути запобіжений жодною мовою. void foo (const A & a), що модифікує об'єкт a, не відрізняється від int countApples (кошик b), який повертає кількість груш. Це просто помилка, помилка, яка збирається будь-якою мовою.
Алессандро Теруцці

55
"Callee вільний відкинути конст ..." uhhhhh ... (° _ °) Це досить мілкий аргумент, який ви тут висловлюєте. Також користувач може просто почати писати місця з випадкової пам'яті 0xDEADBEEF. Але і те, і інше було б дуже погано програмувати, і рано та наполегливо спіймане будь-яким сучасним компілятором. Надалі, пишучи відповіді, будь ласка, не плутайте те, що технічно можливо, використовуючи мову на задньому плані та те, що можливо в реальному реальному коді. Така прихована інформація плутає менш досвідчених читачів і погіршує якість інформації про SO.
Сліпп Д. Томпсон

8
"Зниження в C - це орієнтир;" Посилаючись на джерело для деяких речей, які ви говорите, справді допоможе вашому випадку. У моєму досвіді освіти та галузі цитується твердження, що цитується - звикання в C є настільки ж обов’язковою вбудованою функцією, як і сама система типу - обидва мають задню дверцята, щоб обдурити їх, коли це необхідно (як і справді все в С), але очікується для використання книгою в 99,99% коду.
Сліпп Д. Томпсон

29
Ця відповідь є причиною, чому я іноді ненавиджу дизайн C #. Мовні дизайнери абсолютно впевнені, що вони набагато розумніші за інших розробників і намагаються надмірно захистити їх від помилок. Очевидно, що ключове слово const працює тільки в час компіляції, тому що в C ++ ми маємо прямий доступ до пам'яті і можемо робити все, що завгодно, і є безліч способів отримати круглу декларацію const. Але ніхто не робить цього в звичайних ситуаціях. До речі, "приватні" та "захищені" в C # також не дають гарантій, оскільки ми можемо отримати доступ до цих методів або полів за допомогою відображення.
BlackOverlord

13
@BlackOverlord: (1) Бажання створити мову, властивості якої, природно, призводять розробників до успіху, а не до невдач, мотивується бажанням створити корисні інструменти , а не вірою, що дизайнери розумніші за своїх замовників, як ви гадаєте. (2) Рефлексія не є частиною мови C #; це частина часу виконання; доступ до роздумів обмежений системою безпеки виконання. Коду з низькою довірою не надається можливість робити приватне відображення. Ці гарантії , що надаються модифікатори доступу призначені для низького коду довіри ; високий довірчий код може зробити все, що завгодно.
Ерік Ліпперт

24

Однією з причин того, що в C # немає коректності коректності, є те, що він не існує на рівні виконання. Пам'ятайте, що C # 1.0 не мав жодної функції, якщо вона не була частиною часу виконання.

І кілька причин, чому CLR не має поняття коректності коректності, наприклад:

  1. Це ускладнює час виконання; до того ж у JVM його також не було, і CLR в основному почався як проект зі створення часу, схожого на JVM, а не на C ++ - як час виконання.
  2. Якщо на рівні виконання є коректність коректності, то в BCL має бути правильність const, інакше ця функція майже безглузда, що стосується .NET Framework.
  3. Але якщо BCL вимагає правильності const, кожна мова, що перебуває поверх CLR, повинна підтримувати правильність const (VB, JavaScript, Python, Ruby, F # тощо). Це не відбудеться.

Правильність конкуренції - це майже мовна особливість, наявна лише у C ++. Таким чином, це зводиться до тієї ж аргументації щодо того, чому CLR не вимагає перевірених винятків (що є лише мовою Java).

Крім того, я не думаю, що ви можете запровадити таку функціональну систему системи в керованому середовищі, не порушуючи зворотної сумісності. Тож не розраховуйте на правильність const, коли ви входите у світ C #.


Ви впевнені, що у JVM його немає. Як я знаю, це є. Ви можете спробувати відкритий статичний недійсний TestMethod (остаточний int val) на Java.
інкогніто

2
Фінал насправді не const. Ви все ще можете змінювати поля на посиланні IIRC, тільки не саме значення / посилання. (Яка передається за значенням у будь-якому випадку, тому це більше трюк компілятора, щоб запобігти вам змінити значення параметра.)
Рубен

1
ваша третя куля лише частково правда. мати параметри const для викликів BCL не буде суттєвою зміною, оскільки вони просто більш точно описують контракт. "Цей метод не змінить стан об'єкта" на відміну від "Цей метод вимагає передати значення const", яке було б порушеним
Rune FS

2
Він застосовується всередині методу, тому не було б серйозної зміни впроваджувати його в BCL.
Руна ФС

1
Навіть якщо час запуску не зміг би його застосувати, було б корисно мати атрибут, який визначав, чи потрібно дозволити / очікувати функції для запису до refпараметра (особливо у випадку методів / властивостей структури, this). Якщо метод спеціально стверджує, що він не запише в refпараметр, тоді компілятор повинен дозволяти передавати значення лише для читання. І навпаки, якщо метод стверджує, що він буде писати this, компілятор не повинен дозволяти застосувати такий метод на значення лише для читання.
supercat

12

Я вважаю, що є дві причини C # не коректна.

Перший - зрозумілість . Мало хто з програмістів на C ++ розуміє правильність конкуренції. Простий приклад const int argмилий, але я також бачив char * const * const arg- постійний вказівник на постійні вказівники на непостійні символи. Правильність конкурентоспроможності покажчиків на функції - це зовсім новий рівень затуплення.

Друге - тому, що аргументи класу - це посилання, передані за значенням. Це означає, що вже існує два рівні стійкості, без явно чіткого синтаксису . Аналогічним каменем спотикання є колекції (і колекції колекцій тощо).

Правильність конкуренції є важливою частиною системи типу C ++. Теоретично це може бути додано до C # як те, що перевіряється лише під час компіляції (його не потрібно додавати в CLR і не впливатиме на BCL, якщо б не було включено поняття методів-членів const) .

Однак я вважаю, що це малоймовірно: другу причину (синтаксис) було б досить важко вирішити, що зробило б першу причину (зрозумілістю) ще більшою проблемою.


1
Ви маєте рацію, що CLR насправді не потрібно турбуватися з цього приводу, оскільки можна використовувати modoptі modreqна рівні метаданих, щоб зберігати цю інформацію (як це вже робить C + / CLI - див. System.Runtime.CompilerServices.IsConst); і це може бути банально розширено і до методів const.
Павло Мінаєв

Це варто, але це потрібно робити безпечно. Глибокий / неглибокий const, який ви згадуєте, відкриває цілу банку глистів.
користувач1496062

У c ++, де фактично маніпулюються посиланнями, я бачу ваш аргумент про наявність двох типів const. Однак у C # (де вам потрібно пройти через двері, щоб використовувати "покажчики") мені здається досить зрозумілим, що існує чітко визначене значення для посилальних типів (тобто класів) і чітко визначене, хоча й інше значення для значення типи (тобто примітиви, будови)
асимілятор

2
Програмісти, які не можуть зрозуміти конкурентоспроможність, не повинні програмувати на C ++.
Стів Вайт

5

constозначає "константа часу компіляції" в C #, а не "лише для читання, але можливо, може бути змінена іншим кодом", як у C ++. Приблизний аналог C ++ constу C # є readonly, але це стосується лише полів. Крім цього, у C # взагалі немає поняття правильності const.

Обґрунтування важко сказати напевно, тому що існує маса потенційних причин, але, на мою думку, було б бажання зберегти мову першою і найголовнішою, і невизначені переваги впровадження цієї другої.


Отже, ви вважаєте, що MS вважає, що "шум" має параметри const у методах.
інкогніто

1
Я не впевнений, що таке "шум" - я б швидше назвав це "високим співвідношенням витрат / вигод". Але, звичайно, вам знадобиться хтось із команди C #, який вам точно скаже.
Павло Мінаєв

Тому, як я розумію, ваша аргументація полягає в тому, що стійкість залишилася поза C #, щоб зробити це просто. Подумайте над цим.
Стів Вайт

2

Це можливо, оскільки C # 7.2 (грудень 2017 року з Visual Studio 2017 15.5).

Тільки для структур та основних типів ! , не для членів класів.

Ви повинні використовувати, inщоб надіслати аргумент як вхід за посиланням. Побачити:

Дивіться: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/in-parameter-modifier

Для вашого прикладу:

....
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
}
....
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.