Не повторяйтесь; константы уже являются статическими

На сайте StackOverflow прозвучал еще один интересный вопрос. Представляю его в виде диалога:

В спецификации сказано «хотя константы являются статическими членами, при объявлении модификатор static не требуется и является недопустимым». Почему не принято решение обязать использование модификатора static для констант, раз они и так являются статическими?

Давайте согласимся с тем, что вполне разумно, что константы являются «статическими», поскольку они связаны с самим типом, а не с членами определенного экземпляра. Давайте также согласимся с тем, что без «const» тоже не обойтись. Итак, у нас остается три возможных варианта объявления константы:

1) Сделать модификатор static опциональным: выражения "const int x..." или "static const int x..." оба являются корректными.

2) Сделать модификатор static обязательным: выражение “const int x…” – некорректно, “static const int x…” – корректно.

3) Сделать модификатор static недопустимым: выражение “const int x…” – корректно, выражение “static const x…” – некорректно.

Согласны?

Да. Но почему команда разработчиков языка выбрала вариант (3), а не вариант (1) или (2)?

Об этом ничего не говорится в проектных записях за 1999 год. Но мы можем предположить, что скорее всего творилась в головах проектировщиков языка.

Проблема с вариантом (1) заключается в том, что вы сможете встретить код, который использует оба варианта, и “const int x…” и “static const int y…”, и вы будете, естественно, спрашивать себя «а в чем разница? » А поскольку по умолчанию неконстантные поля и методы являются «экземплярными», а не «статическими», вы сделаете естественный вывод, что некоторые константы относятся к экземпляру, а некоторые – к типу, и этот вывод будет совершенно неверным. Этот вариант является плохим, поскольку он может вводить в заблуждение.

Проблема с вариантом (2) прежде всего, связана с избыточностью. Вам приходится больше набирать, не добавляя при этом ясности или выразительности языка. Кроме того, я не знаю насчет вас, но лично меня просто бесит, когда компилятор выдает мне ошибку «Ты забыл мне сказать волшебное слово. Я знаю об этом, и я совершенно спокойно могу узнать это слово самостоятельно, какое слово требуется в этом случае, но я ничего не буду делать, пока ты не скажешь мне это волшебное слово сам».

Проблема с вариантом (3) состоит в том, что разработчику нужно знать о том, что константность логически подразумевает отношение к типу, а не экземпляру. Однако, как только разработчик выяснит этот факт, проблема исчезнет. Это не такая уж и запутанная идея, которую невероятно сложно понять.

Решение, которое является наиболее дешевым и влечет за собой минимальное количество проблем для конечного пользователя это (3).

Звучит разумно. В языке C# этот принцип устранения избыточности применяется последовательно?

Нет! Очень интересно сравнить и противопоставить эту ситуацию другим возможностям языка, при разработке которых были приняты другие решения.

Например, при перегрузке операторов (overload operators) требуется, чтобы функция была открытой и статической. В этом случае, опять же, мы сталкиваемся с тремя возможными вариантами:

(1) сделать модификаторы public static опциональными,

(2) сделать их обязательными, или

(3) сделать их недопустимыми.

Для перегрузки операторов был выбран вариант (2). Это решение обусловлено тем, что по умолчанию метод является закрытым и экземплярным, поэтому будет весьма странно и неочевидно, чтобы что-то, что выглядит как метод неявно сделать открытым и статическим (что и происходит в вариантах (1) и (3)).

Еще один пример: виртуальный метод с такой же сигнатурой, что и метод базового класса должен содержать модификатор “new” или “override”. Опять три варианта:

(1) сделать этот модификатор опциональным: вы можете написать new, или override, или вообще ничего не написать, в таком случае по умолчанию будет new.

(2) сделать этот модификатор обязательным: вы обязательно должны написать new или override.

(3) сделать модификатор new недопустимым: вы не можете написать new, так что, если вы не написали override, автоматически используется модификатор new.

В этом случае мы остановились на варианте (1), поскольку это лучший способ решения проблем при изменении базовых классов, когда кто-то добавляет виртуальный метод в базовый класс и вы, не зная того переопределяете этот метод. В данной ситуации компилятор выдает предупреждение, а не ошибку.

Каждая из этих ситуаций рассматривалась в отдельности. Не существует общей рекомендации. Нельзя сказать «всегда выбирайте третий вариант», решение, скорее, состоит в том, чтобы выбрать вариант, наименее обманчивый с точки зрения пользователя.

Эрик находится на TechEd. Этот пост записан заранее.

Оригинал статьи