Наверное каждый разработчик достаточно часто сталкивается с кодом вида:
List<Products> productList = GetProductsById(id, 8, 15);
При беглом прочтении часто возникает вопрос: а что обозначают числа 8 и 15?
И если в случае с числовыми или строковыми значениями иногда можно догадаться о их назначении, то булевские зачастую ставят в тупик:
IEmployeeDataSource dataSource = CreateEmployeeDataSource(true);
Или вот такой пример:
this.PostPage (pageId, false);
Давайте рассмотрим варианты избавления кода от подобных "магических" значений. При этом сравним два стандартных способа с одним, специфичным для C# 4.
Применение констант
В этом случае в теле метода или класса добавляется переменная-константа, имя которой поясняет назначение её значение. Например:
const int startAtPage8 = 8;
const int show15ItemsPerPage = 15;
// ...
List<Products> productList = GetProductsById(id, startAtPage8, show15ItemsPerPage);
const bool useLocalStorage = false;
// ...
IEmployeeDataSource dataSource = CreateEmployeeDataSource(useLocalStorage);
const bool postPrintVersion = true;
// ...
this.PostPage (pageId, postPrintVersion);
Как можно заметить, не для всех ситуаций данный вариант хорошо подходит. В частности, для булевских значений сохраняется некая неоднозначность при чтении кода. Это хорошо видно в приведенном примере.
Кроме того, минусом данного подхода является необходимость создания самих констант, что увеличивает число строк кода.
Определение перечисления
Данный способ хорошо подходит для ограниченного набора значений. Например, примеры с булевскими параметрами можно переписать вот так:
enum StorageType { Local = false, Online = true }
// ...
IEmployeeDataSource dataSource = CreateEmployeeDataSource(StorageType.Local);
enum PageVersion { Web = false, Print = true }
// ...
this.PostPage(pageId, PageVersion.Web);
Как правило, для числовых значений создавать перечисления особого смысла нет. А вот приведенные примеры получились гораздо понятнее своих исходных вариантов.
Обратите внимание, что использование перечислений позволяет наложить ограничения на диапазон передаваемых значений. Это может быть как плюсом, так и минусом, т.к. для добавления нового значение необходимо будет изменять перечисление.
Еще один отрицательный момент заключается в необходимости изменения типа параметра или приведения значения перечисления к исходному типу.
Использование именованных параметров
C#, начиная с 4 версии, позволяет указывать имена передаваемых параметров при вызове метода. Это можно использовать как еще один способ повышения читабельности исходного кода.
В данном случае нет необходимости создавать новые переменные или определять перечисления. Достаточно того, чтобы сами параметры функций имели хорошо понятные имена. Давайте посмотрим как в этом случае будет выглядеть код предыдущих примеров:
List<Products> productList = GetProductsById(id, startAtPage: 8, itemsPerPage: 15);
IEmployeeDataSource dataSource = CreateEmployeeDataSource(useOnlineStorage: false);
this.PostPage(pageId, isPrintVersion: false);
Получилось просто, аккуратно, понятно и без необходимости в создании новых сущностей. Кроме того, это еще один повод задуматься над названием параметров при разработке методов.