Лямбда-выражения для рефакторинга ArgumentException
Обновление. Это больше не проблема с С# 6, которая внедрила оператор nameof
для решения таких сценариев (см. MSDN).
Примечание. Обратитесь к разделу Получение имен локальных переменных (и параметров) во время выполнения через лямбда-выражения" для обобщения этот вопрос, а также некоторые ответы.
Мне нравится идея использования лямбда-выражений для создания рефакторизуемых реализаций интерфейса INotifyPropertyChanged
, используя код, аналогичный тому, который предоставляется Eric De Carufel.
Im экспериментирует с реализацией чего-то подобного для предоставления имени параметра ArgumentException
(или его производных классов) в режиме рефакторинга.
Я определил следующий метод утилиты для выполнения проверок null
:
public static void CheckNotNull<T>(Expression<Func<T>> parameterAccessExpression)
{
Func<T> parameterAccess = parameterAccessExpression.Compile();
T parameterValue = parameterAccess();
CheckNotNull(parameterValue, parameterAccessExpression);
}
public static void CheckNotNull<T>(T parameterValue,
Expression<Func<T>> parameterAccessExpression)
{
if (parameterValue == null)
{
Expression bodyExpression = parameterAccessExpression.Body;
MemberExpression memberExpression = bodyExpression as MemberExpression;
string parameterName = memberExpression.Member.Name;
throw new ArgumentNullException(parameterName);
}
}
Проверка правильности аргумента может быть выполнена в режиме рефакторинга с использованием следующего синтаксиса:
CheckNotNull(() => arg); // most concise
CheckNotNull(arg, () => args); // equivalent, but more efficient
Моя забота заключается в следующих строках:
Expression bodyExpression = parameterAccessExpression.Body;
MemberExpression memberExpression = bodyExpression as MemberExpression;
A MemberExpression
представляет "доступ к полю или свойству". Он гарантированно работает корректно в случае INotifyPropertyChanged
, поскольку выражение лямбда будет доступностью свойств.
Однако в моем коде выше выражение лямбда семантически относится к параметру, а не к доступу к полю или свойствам. Единственная причина, по которой работает код, заключается в том, что компилятор С# продвигает любые локальные переменные (и параметры), которые фиксируются в анонимных функциях для переменных экземпляра в классе, создаваемом компилятором, за кулисами. Это подтверждается Jon Skeet.
Мой вопрос: Является ли это поведение (продвижение захваченных параметров в переменные экземпляра) документированным в спецификации .NET, или это просто деталь реализации, которая может измениться в альтернативных реализациях или будущих версиях структуры? В частности, могут быть среды, в которых parameterAccessExpression.Body is MemberExpression
возвращает false
?
Ответы
Ответ 1
Закрытие: как вы сказали, для доступа к параметрам компилятор С# (да, в частности, компилятор) создает класс закрытия, который содержит поля экземпляра для хранения значения вашей захваченной переменной параметра. Может ли это измениться в будущих версиях компилятора С#? Конечно. Возможно, в будущей версии С# генерируемые классы замыкания будут иметь случайные имена переменных, поскольку имя не имеет большого значения во время выполнения. Кроме того, код, который у вас есть, может не работать для других языков .NET. Вы заметите, что VB.NET генерирует деревья выражений и классы закрытия немного иначе, чем С# иногда...
Я не уверен, будет ли ваша текущая реализация работать и для структур (хотя я мог бы неправильно помнить... ситуация, о которой я думаю, касающаяся бокса, может применяться только для Expression<Func<T, object>>
(читайте, пожалуйста, попробуйте сам).
В любом случае... все это говорит... изменится ли он в будущих версиях С#? Возможно нет. Если это так, вы можете изменить свою внутреннюю реализацию, чтобы справиться с ней, вероятно,...
Что касается производительности: пожалуйста, будьте очень осторожны. Вы уже сказали, что было бы более эффективно передавать два аргумента, поэтому вам не нужно компилировать и оценивать лямбда... но просто чтобы быть ясными, вы говорите об ударе от 15 до 30 мс каждый раз, когда вы компилируете и оценить.