使用 nameof 运算符而不是 CallerMemberNameAttribute 来通知 .NET 4.5.3 中的 属性 更改有什么好处吗?

Is there any benefit of using the nameof operator instead of the CallerMemberNameAttribute to notify property changes in .NET 4.5.3?

随着 .NET 4.5.3 的出现,WPF 开发人员现在可以通过三种(或更多)方式通知 INotifyPropertyChanged Interface 属性 更改。基本上,我的问题是 从 from.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));
    }
}

作为 .NET 4.5.3 的一部分在 C#6.0 中引入了第三种也是最新的方法; nameof Operator:

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

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

我自己的假设是,简单地传递字符串的原始的、更容易出错的方法是最有效的,因为我只能想象其他两种方法使用某种形式的反射。但是,我真的很想知道其他两种方法中哪一种更有效,以及在 WPF 上下文中使用 CallerMemberNameAttribute 属性和 nameof 运算符之间是否真的有任何区别。

关于效率:直接使用字符串,CallerMemberNameAttributenameof完全一样,因为字符串是编译器在编译时注入的。没有反射。

我们可以看到使用 TryRoslyn produces this for 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));
    }
}

this for 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 会变得如此流行,使用它会更简单。

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