Expression vs nameof

痞子三分冷 提交于 2019-12-08 19:35:58

问题


It is a good idea to use nameof over expressions for extracting property names?

//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);
}

But the calling is different

public string Url
{
    set
    {
        //with nameof
        RaisePropertyChanged(nameof(Url), this.GetValidationError());
        //with expression
        RaisePropertyChanged(() => Url, this.GetValidationError());
    }
}

What advantages and disadvantages do you know in each approach? Or only execution speed matters ? I mean nameof will be replaced on const value after compilation.


回答1:


Why use expressions to do something you can do on compile time? nameof(Url) is determined on compile time. It costs 0 ms. to evaluate that afterwards. Building an expression is costly and pointless when you can use nameof.

So the bottom line is: don't use expressions unless you really have to (you are already working inside an expression and you have to go from there). Otherwise use nameof.




回答2:


In summary...

...use nameof...

...when you just want the property name.

...or use an expression tree...

...when you need to:

  • ...introspect the selected member.
  • ...get who declares the whole member.
  • ...get the namespace of who declares the whole member.
  • ...and so on.

Since an expression tree is just a data structure unless you compile it into a delegate, it exposes the selected member as a MemberInfo (i.e. MethodInfo, PropertyInfo...) which enables you to do reflection.

Unless you need that, go with nameof.




回答3:


nameof() vs. Expression :

If you just need the name of the property as a string, then you can use nameof(). But if you aim to get the value of that property for some objects, then use expression (Details below).

Expression as a property selector is used in generic methods in order to select a property from an object T. As in the IQueryable's Select Statement. I doesn't make any sense in that case to use nameof(), because the Expression can make it safely compile that the property selected is part of the object T.Then expression is then used by db providers or Enumerable (aka Linq) to provide the result.

As for RaisePropertyChanged, i think the overload with Expression was useful before C# 6 (when nameof() was introduced) and it is still there for backward compatibility maybe. Using nameof() in this case is much, much faster.

Example to clarify where Expression beats nameof() :

(Note, Func is used for simplicity. The idea is in how to call the method)

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;
        }
    }
}

Usage:

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

While here we have a trouble:

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



回答4:


I would recommend using string combined with the [CallerMemberName] attribute.

protected void RaisePropertyChanged([param: Localizable(true)] string validationError, [CallerMemberName]string propertyName = null)
{
    // whatever
}

You have to position the propertyName at the end of the parameters, because of the default value, that has to be null/string.Empty.

The compiler will replace all calls where propertyName is not provided by the caller with the string of the current property you're in:

public string SomeString
{
  get { return _someString}
  set 
  { 
    _someString = value;
    OnPropertyChanged("your validation error");
  }
}

will automatically be converted to:

public string SomeString
{
  get { return _someString}
  set 
  { 
    _someString = value;
    OnPropertyChanged("your validation error", "SomeString");
  }
}

by the compiler!

If you wan't to use it outside of a property, you can call it with nameof(SomeString).

I would recommend it over the propertySelector because it is compile time, and doesn't cost you cpu-cycles at runtime. It is also refactoring save other than calling it with a string directly.

When you really need more information about the caller, use propertySelector with expression trees. But there is no need to use cpu-cycles when you can do something at runtime.

For example, the OnPropertyChanged-implementation in Prism by the Microsoft Pattern and Practices team looks like this:

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);
}

But there is also the propertyExpression version:

protected virtual void OnPropertyChanged<T>(Expression<Func<T>> propertyExpression)
{
  this.OnPropertyChanged(PropertySupport.ExtractPropertyName<T>(propertyExpression));
}

Which only does more work, because it needs to extract the name over reflection (performance hit) and then calls the original implemenation with the string as parameter.

Thats why nameof() and/or CallerMemberName are preferable.



来源:https://stackoverflow.com/questions/38324208/expression-vs-nameof

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!