6

nameof使用overexpressions提取属性名称是个好主意吗?

//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

4 回答 4

12

为什么要使用表达式来做一些你可以在编译时做的事情?nameof(Url)由编译时间决定。它花费0毫秒。之后进行评估。当您可以使用nameof.

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

于 2016-07-12T09:01:17.357 回答
4

总之...

...使用nameof...

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

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

...当您需要:

  • ...内省选定的成员。
  • ...获取谁声明了整个成员。
  • ...获取声明整个成员的名称空间。
  • ...等等。

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

除非您需要,否则请使用nameof.

于 2016-07-12T09:09:25.230 回答
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 周期。除了直接用字符串调用它之外,它还在重构 save 。

当您确实需要有关调用者的更多信息时,请使用带有表达式树的 propertySelector。但是当你可以在运行时做某事时,就不需要使用 cpu-cycles。

例如,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更可取的原因。

于 2016-07-12T10:38:09.247 回答
1

nameof()Expression

如果您只需要将属性名称作为字符串,那么您可以使用nameof(). 但是,如果您的目标是获取某些对象的该属性的值,请使用表达式(详细信息如下)。

表达式作为属性选择器用于泛型方法中,以便从对象中选择属性T。就像在IQueryable's Select 语句中一样。在这种情况下使用 没有任何意义nameof(),因为表达式可以安全地编译所选属性是对象 T 的一部分。然后表达式由数据库提供程序或Enumerable(又名 Linq)使用来提供结果。

至于RaisePropertyChanged,我认为重载 withExpression在 C# 6(引入时)之前很有用,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));

在这里我们遇到了麻烦:

// Doesn't Compile, No Property1 found in x.
IEnumerable<int> results = arr.MySelect(x => x.Property1);

// Compiles and throw a run-time error as there is no Message property in string.
IEnumerable<int> results2 = arr.MySelect2<string, int>(nameof(Exception.Message));
于 2016-07-12T09:09:34.723 回答