在 WPF(以及更广泛的 .NET 数据绑定场景)中,INotifyPropertyChanged  是一个极其重要的接口,它用于实现 属性更改通知机制 ,是 数据绑定能够正常工作(尤其是双向绑定)的核心基础。
一、🔷 什么是 INotifyPropertyChanged?
INotifyPropertyChanged是一个接口,定义如下:
public interface INotifyPropertyChanged { event PropertyChangedEventHandler? PropertyChanged; }
        它包含一个事件:
- 
PropertyChanged :当某个 绑定属性的值发生变化时 ,类需要触发这个事件,并传入发生变化的属性名(通常是nameof(PropertyName))。 
WPF 的绑定系统(如 {Binding Name})会 监听这个事件 ,一旦属性值变了,就会 自动更新 UI。
二、🔷 类级别实现(推荐 ✅ 通用做法)
这是 最常见、最推荐、最可维护  的方式:在类中实现 INotifyPropertyChanged 接口,并提供一个通用的方法(如 OnPropertyChanged)来触发属性变更通知。
✅ 示例:类级别实现 INotifyPropertyChanged
using System.ComponentModel;
using System.Runtime.CompilerServices;
public class Person : INotifyPropertyChanged
{
    private string _name = "";
    private int _age;
    // 属性
    public string Name
    {
        get => _name;
        set
        {
            if (_name != value)
            {
                _name = value;
                OnPropertyChanged(); // 调用通用方法通知变更
            }
        }
    }
    public int Age
    {
        get => _age;
        set
        {
            if (_age != value)
            {
                _age = value;
                OnPropertyChanged();
            }
        }
    }
    // INotifyPropertyChanged 成员
    public event PropertyChangedEventHandler? PropertyChanged;
    // 通用方法,用于触发属性更改通知
    protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
        ✅ 优点:
- 
代码复用性高 :通过
OnPropertyChanged()方法统一处理通知逻辑; - 
简洁 :每个属性只需要在 set 中判断值是否变化,然后调用
OnPropertyChanged(); - 
可维护性强:新增属性时只需复制模式,不易遗漏通知;
 - 
CallerMemberName 特性 :自动获取调用属性的名字,避免硬编码
nameof(Name); 
三、🔷 属性级别实现(不推荐 ❌,仅作了解)
你提问中也提到了"属性级别实现",这通常指的是:
不在类中统一实现 INotifyPropertyChanged,而是对每一个属性单独处理通知逻辑,甚至不使用接口,而是手动去触发某种通知(不标准)。
但严格来说,INotifyPropertyChanged 是一个类级别的接口,你无法在"属性级别"实现它,因为接口本身是绑定到类上的。你只能:
- 
在类中实现 INotifyPropertyChanged 接口,
 - 
然后 针对每个属性,决定是否要触发 PropertyChanged 事件。
 
所以,如果你听到有人说"属性级别实现 INotifyPropertyChanged",通常指的是:
在类的某个具体属性的 setter 中手动触发 PropertyChanged,而不是通过统一的辅助方法。
❌ 不推荐的写法(非统一管理,不推荐)
public class Person
{
    private string _name = "";
    public string Name
    {
        get => _name;
        set
        {
            if (_name != value)
            {
                _name = value;
                // 手动触发事件(不经过统一方法,容易出错)
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name)));
            }
        }
    }
    public event PropertyChangedEventHandler? PropertyChanged;
}
        ❗ 问题:
没有实现
INotifyPropertyChanged接口(编译不会报错,但绑定系统无法识别);如果你确实实现了接口,但每个属性都手动写
PropertyChanged.Invoke(...),代码冗余、易错;没有利用
[CallerMemberName],每次都要写死nameof(PropertyName);
四、🔷 为什么推荐"类级别统一实现"?
| 对比项 | 类级别统一实现(推荐) | 属性级别手动实现(不推荐) | 
|---|---|---|
| 是否实现 INotifyPropertyChanged | ✅ 是 | ❌ 通常没实现,或实现了但不规范 | 
| 代码复用性 | ✅ 高,通过一个方法统一处理 | ❌ 低,每个属性都要写重复代码 | 
| 可维护性 | ✅ 易于扩展,新增属性轻松 | ❌ 每次新增属性都要小心翼翼 | 
| 是否使用 CallerMemberName | ✅ 推荐使用,避免硬编码属性名 | ❌ 通常要手动写 nameof(...) | 
| 绑定系统兼容性 | ✅ 完美支持 WPF 数据绑定 | ❌ 若未实现接口,绑定不会自动更新 | 
| 推荐程度 | ✅ 强烈推荐 | ❌ 不推荐,仅用于理解原理 | 
五、🔷 进阶:使用基类或代码片段简化
为了进一步简化代码,很多开发者会:
1. 定义一个基类 BaseModel / ViewModelBase
public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
        然后让你的 ViewModel / Model 类继承它:
        public class Person : ViewModelBase
{
    private string _name = "";
    public string Name
    {
        get => _name;
        set
        {
            if (_name != value)
            {
                _name = value;
                OnPropertyChanged(); // 直接调用基类方法
            }
        }
    }
}
        2. 使用 Fody.PropertyChanged(强烈推荐 ⭐⭐⭐⭐⭐)
如果你不想手写任何 OnPropertyChanged(),可以使用第三方库:
🔗 Fody.PropertyChanged
- 
它是一个 编译时织入(AOP)工具 ,在你编译项目时,自动为所有标记了
[AddINotifyPropertyChangedInterface]的类,或所有属性,注入 INotifyPropertyChanged 代码。 - 
你 无需手写任何 PropertyChanged 代码!
 
📦 安装后,你只需这样写:
[AddINotifyPropertyChangedInterface]
public class Person
{
    public string Name { get; set; } = "";
    public int Age { get; set; }
}
        ✅ 编译后,Fody 会自动为 Name 和 Age 加上属性变更通知逻辑,无需手动实现 INotifyPropertyChanged!
六、🔷 总结
| 项目 | 说明 | 
|---|---|
| INotifyPropertyChanged 是什么? | 一个接口,用于在属性值更改时通知绑定系统(WPF 数据绑定的核心) | 
| 类级别实现(推荐) | 在类中实现 INotifyPropertyChanged,提供统一的 OnPropertyChanged() 方法,每个属性 setter 中调用它 | 
| 属性级别实现 ❌(不标准) | 一般指在每个属性中手动触发 PropertyChanged,但未统一管理,不推荐 | 
| 最佳实践 | 使用统一的 OnPropertyChanged() 方法,或继承自 ViewModelBase,或使用 Fody.PropertyChanged 自动生成 | 
| 绑定生效前提 | 数据对象必须实现 INotifyPropertyChanged,且属性变更时正确触发事件,UI 才会自动更新 | 
✅ 推荐做法(总结步骤)
- 
让你的数据类(Model / ViewModel)实现 INotifyPropertyChanged
 - 
提供 OnPropertyChanged 方法(最好用 CallerMemberName)
 - 
在每个属性的 setter 中判断值是否真的变化,若变化则调用 OnPropertyChanged()
 - 
(可选)使用基类 ViewModelBase 或 Fody.PropertyChanged 进一步简化代码