最好使用nameof
而不是expressions
来提取属性名称?
//method with expression
protected void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpression, bool isValid, [param: Localizable(true)] string validationError)
{
string propertyName = PropertySupport.ExtractPropertyName(propertyExpression);
RaisePropertyChanged(propertyName, isValid, validationError);
}
//the same logic without expression
protected void RaisePropertyChanged(string propertyName, [param: Localizable(true)] string validationError)
{
RaisePropertyChanged(propertyName, validationError == null, validationError);
}
但是呼叫是不同的
public string Url
{
set
{
//with nameof
RaisePropertyChanged(nameof(Url), this.GetValidationError());
//with expression
RaisePropertyChanged(() => Url, this.GetValidationError());
}
}
您了解每种方法有哪些优点和缺点?或者只有执行速度很重要?我的意思是编译后nameof
值将替换为const
。
答案 0 :(得分:12)
为什么要使用表达式来执行编译时可以执行的操作? nameof(Url)
在编译时确定。它花费0毫秒。之后评估。使用nameof
时,构建表达式既昂贵又毫无意义。
所以底线是:不要使用表达式,除非你真的必须(你已经在表达式中工作,你必须从那里开始)。否则使用nameof
。
答案 1 :(得分:3)
总结......
nameof
... ...当你只想要属性名称时。
......当你需要:
由于表达式树只是一个数据结构,除非您将其编译为委托,它将所选成员公开为MemberInfo
(即MethodInfo
,PropertyInfo
...)使您能够进行反射。
除非您需要,请使用nameof
。
答案 2 :(得分:1)
nameof()
与Expression
:如果您只需要将该属性的名称作为字符串,则可以使用nameof()
。但是如果你的目标是为某些对象获取该属性的值,那么使用表达式(下面的详细信息)。
表达式作为属性选择器用于泛型方法,以便从对象T
中选择属性。与IQueryable
的Select Statement一样。在这种情况下我使用nameof()
没有任何意义,因为Expression可以安全地编译所选属性是对象T的一部分。然后db提供者使用表达式或{{1} (又名Linq)提供结果。
对于Enumerable
,我认为RaisePropertyChanged
的重载在C#6(引入Expression
之前)之前很有用,并且它仍然可以向后兼容。在这种情况下使用nameof()要快得多。
澄清nameof()
击败Expression
的位置的示例:
(注意,nameof()
用于简单。想法是如何调用方法)
Func
用法:
public static class MyExtensions
{
public static IEnumerable<K> MySelect<T, K>(this IEnumerable<T> source,
Func<T, K> selector)
{
foreach(T item in source)
{
yield return selector(item);
}
}
public static IEnumerable<K> MySelect2<T, K>(this IEnumerable<T> source,
string propertyName)
{
foreach (T item in source)
{
// K value = GET VALUE BY REFLECTION HERE THROUGH T AND PROPERTY NAME;
yield return value;
}
}
}
虽然我们遇到了麻烦:
// Fancy Intellisense when typing x. with compile time type inference
IEnumerable<int> results = arr.MySelect(x => x.Length);
// No so pretty, you need to explictly specify the type and you can pass whatever string
IEnumerable<int> results2 = arr.MySelect2<string, int>(nameof(string.Length));
答案 3 :(得分:1)
我建议将字符串与[CallerMemberName]
属性结合使用。
protected void RaisePropertyChanged([param: Localizable(true)] string validationError, [CallerMemberName]string propertyName = null)
{
// whatever
}
您必须将propertyName放在参数的末尾,因为默认值必须是null
/ string.Empty
。
编译器将使用您所在的当前属性的字符串替换调用者未提供propertyName的所有调用:
public string SomeString
{
get { return _someString}
set
{
_someString = value;
OnPropertyChanged("your validation error");
}
}
将自动转换为:
public string SomeString
{
get { return _someString}
set
{
_someString = value;
OnPropertyChanged("your validation error", "SomeString");
}
}
由编译器!
如果您不想在酒店外使用它,可以使用nameof(SomeString)
进行调用。
我会推荐它在propertySelector上,因为它是编译时间,并且在运行时不会花费你cpu-cycles。除了直接用字符串调用它之外,它还进行重构保存。
当您确实需要有关调用者的更多信息时,请将propertySelector与表达式树一起使用。但是,当你可以在运行时做某事时,不需要使用cpu-cycle。
例如,Microsoft Pattern and Practices团队在Prism中的OnPropertyChanged实现如下所示:
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
// ISSUE: reference to a compiler-generated field
PropertyChangedEventHandler changedEventHandler = this.PropertyChanged;
if (changedEventHandler == null)
return;
PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);
changedEventHandler((object) this, e);
}
但是还有propertyExpression版本:
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> propertyExpression)
{
this.OnPropertyChanged(PropertySupport.ExtractPropertyName<T>(propertyExpression));
}
只做更多工作,因为它需要通过反射(性能命中)提取名称,然后使用字符串作为参数调用原始实现。
这就是为什么nameof()
和/或CallerMemberName
更可取。