Страницы

Поиск по вопросам

суббота, 7 декабря 2019 г.

Локальная константа времени выполнения

#c_sharp #константа #дизайн_языка


В C++ для определения локальной константы времени выполнения можно написать так:

const auto c = f();


Далее, все попытки изменить c будут приводить к ошибке компиляции.

В C# такой возможности нет. Можно использовать readonly член подобным образом, но
не локальную константу. 

Почему существует такое ограничение (в чём причина отсутствия локальных констант
а-ля C++) и какое каноническое решение имеется для обеспечения локальной константности
времени выполнения на C#? Неужели для этого надо создавать отдельный read-only интерфейс?
    


Ответы

Ответ 1



Стоит отметить, что по локальным readonly-переменным есть официальный proposal. Фича опоздала к C# 7, но у нее все шансы войти в C# 8. Со стороны CLR для реализации нет никаких ограничений - readonly locals, как и все фичи C# со времен .NET 4.0 - это compile-time фичи - т.к. версия рантайма с тех пор не поменялась. По сути, proposal сводится к нескольким пунктам: возможности помечать параметры как readonly возможности помечать переменные как readonly шорткату let - эквиваленту readonly var Локальные readonly, как и неизменяемые foreach iteration variable - это, прежде всего, защита от дурака/ошибки копипасты. И уже потом - способ указать компилятору на возможную оптимизацию замыканий. На мой взгляд, сама по себе возможность вручную приписывать readonly к локальным переменным только ради в качестве защиты от потенциальной ошибки - неудобна для реального использования. Достаточно вспомнить о final в Java. Поэтому без шортката let фича будет достаточно бесполезной - ее будут использовать фанаты "безопасного кода" - те, кто сейчас принципиально не использует var и явно вписывают типы во всех упоминаниях генерика. Остальные пойдут по пути наименьшего сопротивления - гораздо дешевле и проще исправить одну ошибку раз в месяц, чем приписывать readonly к каждой переменной. Ну и опять же, тесты, при правильном применении, решают проблему ошибок копипасты. Более надежный способ защиты реализован, например, в F#, где локальные значения по умолчанию неизменяемы, а = вне объявления значения вообще не работает как оператор присвоения. let x = 1 x = 2 // This expression should have type 'unit', but has type 'bool' Для изменения значения разработчику придется сделать дополнительные телодвижения, как в строчке где переменная объявлена, так и в строчке, где переменная изменяется. let mutable x = 1 x <- 2 Сам язык подталкивает его к тому, чтобы объявить еще одно значение, а не менять существующее. Одна из трех добродетелей программиста - лень - уберегает его от ошибки. А просто так дописывать readonly к переменным, "как бы чего не вышло" - никто, кроме особых фанатов, не будет - из соображений той же лени.

Ответ 2



Ответы: JaredPar, и Jon Skeet Одна из причин - отсутствие поддержки CLR для локальных переменных только для чтения. Readonly переводится в CLR/CLI initonly опкод. Этот флаг может быть применен только к полям и не имеет смысла для локальных переменных. Фактически, применение этого кода к локальным переменным скорее всего сделает код непроверяемым. Это не значит, что C# не может реализовать это. Но это добавляет два смысла для одной и той же языковой конструкции. Версия для локальных переменных не будет иметь в CLR эквивалентного отображения. Обращаясь к ответу Jared'а, это возможно может быть compile-time фичей - компилятор будет запрещать тебе писать в переменную после первоначального объявления (которое должно включать присвоение). Есть ли в этом польза? Потенциальная - но не большая, если быть честным. Если ты не можешь сказать будет или нет присваивание еще где-то в методе, то твой метод слишком большой. Как бы то ни было, в Java есть эта особенность (при использовании модификатора final) и я очень редко видел его использование в случаях отличных от разрешения использования переменной в внутреннем анонимном классе - и где это было использовано это давало скорее впечатление беспорядка, чем полезной информации. в качестве обходного пути, если очень хочется, только на свой страх и риск есть такой: Настолько отличный, насколько ужасный пример В качестве run-time константы можно использовать переменную в блоке итератора: public void f() { foreach (int n in new int[] { getNum() }) // n declared constant { n = 3; // won't compile: "error CS1656: Cannot assign to 'n' because it is a 'foreach iteration variable'" } }

Комментариев нет:

Отправить комментарий