При написании кода иногда бывает необходимо использовать строковые имена свойств классов с целью последующего обращения к указанным свойствам. Примером такого подхода являются методы класса BaseObject (методы GetPropertyValue, SetPropertyValue , GetCollection и другие). Сигнатуры таких методов выглядят следующим образом:

При вызове указанных методов в качестве параметра передается строковое наименование свойства, к которому необходимо получить доступ:

Такой подход обладает следующими недостатками:

  • При кодировании легко допустить ошибку в имени свойства, которая проявится только в процессе выполнения при доступе к свойству. Такая ошибка может существовать долгое время, пока не будет обнаружена.
  • При сопровождении продукта в результате рефакторинга легко изменить имя свойства, но не поменять его наименование, переданное в качестве параметра в виде строкового литерала (инструменты для рефакторинга не выполняют автоматически такие переименования). В результате получаем несогласованный код.

Для борьбы с описанными недостатками применен подход с использованием лямбда-выражений для получения имени свойства. Описываемый подход показан на примере класса BaseObjectEx<T>, который входит в состав демонстрационного примера  LambdaToGetPropName к статье. Данный класс является наследником BaseObject. В классе BaseObjectEx<T> перегружены методы, принимающие в качестве параметра имя свойства (это ранее упомянутые методы GetPropertyValue, SetPropertyValue , GetCollection и другие). Исходный код таких методов выглядит следующим образом:

В качестве параметра, указывающего на свойство класса, передается лямбда-выражение доступа к свойству, которое имеет вид:

С целью упрощения использования данного подхода при разработке реализован вспомогательный статический класс MemberHelper. С помощью метода MemberName можно безопасно в отношении типов получить строковое наименование свойства произвольного типа:

Вызов перегруженной версии методов теперь выглядит следующим образом:

Из примера видно, что теперь при вызове методов GetPropertyValue и SetPropertyValue для указания свойства используется лямбда-выражение.

Аналогичным образом выполняется вызов и других переопределенных методов, таких, как GetMemberValue, SetMemberValue, GetList, SetDelayedPropertyValue, GetDelayedPropertyValue, GetCollection.

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

Также при рефакторинге осуществляется полное и корректное переименование свойств классов.

Однако, описанный подход обладает и недостатком - это его быстродействие. Результаты профилирования показали, что на генерацию лямбда-выражения при каждом вызове одного из перегруженных методов тратится слишком много времени. Результаты замеров расходуемого времени на 1 миллион вызовов следующие:

  • 1000000 вызовов метода GetPropertyValue<TProperty>("SomeProperty") - 0.37 секунды;
  • 1000000 вызовов метода GetPropertyValue(x=>x.SomePproperty) - 5.9 секунды;

Т.е. использование лямбда выражений в данном применении медленнее приблизительно в 16 раз.
Поэтому в критичным к производительности участках использовать данный метод может оказаться неприемлемо.

Демонстрационный пример: LambdaToGetPropName