Подтвердить что ты не робот

Есть ли какая-либо польза от использования оператора nameof вместо CallerMemberNameAttribute для уведомления изменений свойств в .NET 4.5.3?

С появлением .NET 4.5.3 разработчики WPF теперь имеют три (или более) способа уведомления INotifyPropertyChanged Interface изменения свойств. В основном, мой вопрос: какой из двух методов, введенных в .NET 4.5, является более эффективным способом уведомления об изменениях свойств и может ли любой из них иметь какую-либо выгоду при использовании в WPF?

Фон

Для тех, кто не знаком с этим предметом, вот основные три метода. Первый - это оригинальный, более подверженный ошибкам метод простого пропускания строки:

public string TestValue
{
    get { return testValue; }
    set { testValue = value; NotifyPropertyChanged("TestValue"); }
}

protected virtual void NotifyPropertyChanged(string propertyName)
{
    if (PropertyChanged != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

Второй метод был введен в .NET 4.5; CallerMemberNameAttribute:

public string TestValue
{
    get { return testValue; }
    set { testValue = value; NotifyPropertyChanged(); }
}

protected virtual void NotifyPropertyChanged([CallerMemberName]string propertyName = "")
{
    if (PropertyChanged != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

Третий и самый последний метод был (или скоро будет) представлен в С# 6.0 как часть .NET 4.5.3; nameof Оператор:

public string TestValue
{
    get { return testValue; }
    set { testValue = value; NotifyPropertyChanged(nameof(TestValue)); }
}

protected virtual void NotifyPropertyChanged(string propertyName)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

Мое собственное предположение заключалось бы в том, что самый эффективный метод с ошибкой простой передачи строки был бы наиболее эффективным, поскольку я могу только представить себе, что другие два метода используют некоторую форму отражения. Тем не менее, я очень хочу узнать, какой из двух других методов более эффективен и действительно ли будет какая-либо разница между использованием атрибута CallerMemberNameAttribute и оператора nameof в контексте WPF.

4b9b3361

Ответ 1

Об эффективности: использование строки напрямую, CallerMemberNameAttribute, nameof - все точно такие же, поскольку строка вводится компилятором во время компиляции. Там не было отражения.

Мы видим, что с помощью TryRoslyn производит это для CallerMemberNameAttribute:

public string TestValue
{
    get { return this.testValue; }
    set { this.testValue = value; this.NotifyPropertyChanged("TestValue"); }
}
protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
    if (this.PropertyChanged != null)
    {
        this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

И для nameof:

public string TestValue
{
    get { return this.testValue; }
    set { this.testValue = value; this.NotifyPropertyChanged("TestValue"); }
}
protected virtual void NotifyPropertyChanged(string propertyName)
{
    if (this.PropertyChanged != null)
    {
        this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

Так как во время выполнения все параметры - это просто string, нет проблем с контекстом WPF.

Об удобстве: CallerMemberNameAttribute требуется иметь необязательный параметр, а nameof - нет, но nameof требует указать свойство, а CallerMemberNameAttribute - нет.

Я предсказываю, что nameof окажется настолько популярным, что было бы гораздо проще использовать его вместо этого.

Ответ 2

CallerMemberNameAttribute может использоваться только для вызываемой функции, чтобы получить имя вызывающей функции.

Оператор nameof выходит за рамки этого. Его можно использовать где угодно.

Если вы хотите рассуждать об этом только в области привязки данных WPF, возьмите этот пример:

public string FullName
{
   get
   {
       return string.Format(
           "{0} {1}",
           this.firstName,
           this.lastName);
   }
}

public string FirstName
{
   get
   {
       return this.firstName;
   }
   set
   {
       if (value != this.firstName)
       {
           this.firstName = value;
           NotifyPropertyChanged(nameof(FirstName));
           NotifyPropertyChanged(nameof(FullName));
        }
   }
}

public string LasttName
{
   get
   {
       return this.lastName;
   }
   set
   {
       if (value != this.lastName)
       {
           this.lastName = value;
           NotifyPropertyChanged(nameof(LasttName));
           NotifyPropertyChanged(nameof(FullName));
        }
   }
}