表达与名称

时间:2016-07-12 08:57:02

标签: c# .net expression c#-6.0 nameof

最好使用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

4 个答案:

答案 0 :(得分:12)

为什么要使用表达式来执行编译时可以执行的操作? nameof(Url)在编译时确定。它花费0毫秒。之后评估。使用nameof时,构建表达式既昂贵又毫无意义。

所以底线是:不要使用表达式,除非你真的必须(你已经在表达式中工作,你必须从那里开始)。否则使用nameof

答案 1 :(得分:3)

总结......

...使用nameof ...

...当你只想要属性名称时。

...或使用表达式树...

......当你需要:

  • ...内省所选成员。
  • ...告诉谁宣布整个会员。
  • ...获取声明整个成员的名称空间。
  • ......等等。

由于表达式树只是一个数据结构,除非您将其编译为委托,它将所选成员公开为MemberInfo(即MethodInfoPropertyInfo ...)使您能够进行反射

除非您需要,请使用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更可取。

相关问题