Expression vs nameof
Asked Answered
E

4

6

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.

Epigenous answered 12/7, 2016 at 8:57 Comment(1)
I would say it depends on the situation. In simple scenarios nameof will generally meet your needs whereas expressions could be used for dynamic and complex queries .Chaiken
G
12

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.

Grenadines answered 12/7, 2016 at 9:1 Comment(1)
Just a small addition: using an expression can lead to runtime exceptions if you're not careful, while nameof will give you compile time errors.Babu
P
4

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.

Pomcroy answered 12/7, 2016 at 9:9 Comment(0)
B
1

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));
Bridewell answered 12/7, 2016 at 9:9 Comment(1)
Keep in maind OnPropertyChanged can be used with the CallerMemberName attribute, which imo beats both of the above listed ways when only the caller name is required. Microsoft in Prism from the Patterns and Practices team implemented both, the propertyselector and the CallerMemberName, into their ViewModelBase. I think it doesn't really matter (except that callerMemberName and nameof are compile time, expressions are runtime, which is a performance hit), and it's all about preference.Apathetic
A
1

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.

Apathetic answered 12/7, 2016 at 10:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.