【问题标题】:How to implement INotifyPropertyChanged with nameof rather than magic strings?如何使用 nameof 而不是魔术字符串实现 INotifyPropertyChanged?
【发布时间】:2025-12-15 02:10:02
【问题描述】:

我正在阅读 C# 6 中新的 nameof 关键字。 我想知道如何使用这个关键字实现INotifyPropertyChanged,先决条件是什么(当然除了 C# 6 之外)以及它将如何影响我的 MVVM 应用程序的性能?

【问题讨论】:

  • 魔术字符串问题已经有替代方案了,你知道的。虽然nameof 应该正式化。
  • 不,我从来没有指出这一点。我只是通知你,以防你在等待尚未发布的东西来解决你今天可以解决的问题。它对 其他 访问者也很有用,因为这个问题不仅仅是为了您的利益。
  • 感谢您的信息。 ??????
  • 有用的 RegEx 替换 Visual Studio 中的所有内容:从 OnPropertyChanged("(?<propName>\w+)"\);OnPropertyChanged(nameof(${propName}));

标签: c# mvvm inotifypropertychanged c#-6.0


【解决方案1】:

See the documentation for INotifyPropertyChanged.PropertyChanged Event

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

【讨论】:

    【解决方案2】:

    我发现使用 PropertyChanged.Fody 会容易得多,因为您最终会出现更少的错误和更干净的代码,请参阅 - https://github.com/Fody/PropertyChanged

    你所要做的就是用ImplementPropertyChanged属性标记你的类:

    [ImplementPropertyChanged]
    public class Person 
    {        
        public string GivenNames { get; set; }
        public string FamilyName { get; set; }
    
        public string FullName
        {
            get
            {
                return string.Format("{0} {1}", GivenNames, FamilyName);
            }
        }
    }
    

    在构建之后它被转换为:

    public class Person : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        string givenNames;
        public string GivenNames
        {
            get { return givenNames; }
            set
            {
                if (value != givenNames)
                {
                    givenNames = value;
                    OnPropertyChanged("GivenNames");
                    OnPropertyChanged("FullName");
                }
            }
        }
    
        string familyName;
        public string FamilyName
        {
            get { return familyName; }
            set
            {
                if (value != familyName)
                {
                    familyName = value;
                    OnPropertyChanged("FamilyName");
                    OnPropertyChanged("FullName");
                }
            }
        }
    
        public string FullName
        {
            get
            {
                return string.Format("{0} {1}", GivenNames, FamilyName);
            }
        }
    
        public virtual void OnPropertyChanged(string propertyName)
        {
            var propertyChanged = PropertyChanged;
            if (propertyChanged != null)
            {
                propertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
    

    【讨论】:

    • 很棒的帮手。我使用很多 POCO 与 ServiceStack 进行数据交换,这让我可以让它们保持超级简单(代码方面)。
    • 如果您包含对 Fody 是什么的解释,这个答案会更好。此外,转换后的代码中仍然有魔术字符串,这是 OP 想要避免的。如果更改其中一个属性的名称,Fody 是否也会自动更新字符串?
    • 所以 fody 是一个 post build il weaver,所以它会在构建后更改您的代码......每次构建后都会自动更新魔术字符串
    【解决方案3】:

    这是使用新 C# 6.0 糖的类的完整代码示例:

    public class ServerViewModel : INotifyPropertyChanged {
        private string _server;
        public string Server {
            get { return _server; }
            set {
                _server = value;
                OnPropertyChanged(nameof(Server));
            }
        }
    
        private int _port;
        public int Port {
            get { return _port; }
            set {
                _port = value;
                OnPropertyChanged(nameof(Port));
            }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName) => 
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    

    这样,您将获得nameof() 运算符、空条件运算符?. 和表达式体函数(OnPropertyChanged 定义)。

    【讨论】:

      【解决方案4】:

      看起来像这样:

      public string Foo
      {
         get
         {
            return this.foo;
         }
         set
         {
             if (value != this.foo)
             {
                this.foo = value;
                OnPropertyChanged(nameof(Foo));
             }
         }
      }
      

      nameof(Foo) 在编译时将被替换为“Foo”字符串,因此它应该非常高效。这不是反射。

      【讨论】:

      • 具有讽刺意味的是,这比使用CallerMemberName 的其他机制更多 代码,不幸的是它不像infoof 那样滑稽。
      • CallerMemberName 仅在 .NET 4.5 中可用。 Nameof 带有 C# 编译器,因此您仍然可以针对较旧的 .NET 框架。
      • 不错,没想到这个好处。但有趣的是,CallerMemberName 也是一个 compiler-powered feature,因此可以从 .NET 4.5 依赖项中取出。我个人会更改为nameof,另一个原因是代码更加明确,并且从调用者的角度来看更少隐藏。
      • 我想nameof(Foo) 就足够了,因为该属性肯定在范围内。如果您出于某种原因想要获得资格,对于非静态成员来说,nameof(this.Foo) 不是比nameof(MyClass.Foo) 更好吗?我没有尝试实际编译任何这些。
      【解决方案5】:

      这只是使用nameof() 而不是魔术字符串的问题。下面的例子来自我的blog article主题:

      private string currentTime;
      
      public string CurrentTime
      {
          get
          {
              return this.currentTime;
          }
          set
          {
              this.currentTime = value;
              this.OnPropertyChanged(nameof(CurrentTime));
          }
      }
      

      由于它是evaluated at compile-time,因此它比当前的任何替代方案(在博客文章中也提到)都具有更高的性能。

      【讨论】:

      • 感谢您的回答。我真的很喜欢你的博客文章
      最近更新 更多