Что такое INotifyPropertyChanged

Ответ

INotifyPropertyChanged — это ключевой интерфейс в .NET для реализации шаблона наблюдатель (Observer) при связывании данных (Data Binding). Он позволяет объекту (например, ViewModel) уведомлять UI-слой (View) об изменении значения своего свойства, что автоматически приводит к обновлению интерфейса.

Базовая реализация:

using System.ComponentModel;
using System.Runtime.CompilerServices;

public class ProductViewModel : INotifyPropertyChanged
{
    private decimal _price;
    public decimal Price
    {
        get => _price;
        set
        {
            // Проверка на реальное изменение значения для оптимизации
            if (_price == value) return;
            _price = value;
            // Уведомление системы привязки данных
            OnPropertyChanged();
            // Можно вызвать связанные вычисления
            OnPropertyChanged(nameof(PriceWithTax));
        }
    }

    public decimal PriceWithTax => _price * 1.20m;

    public event PropertyChangedEventHandler? PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Практические советы:

  1. Используйте [CallerMemberName]: Этот атрибут автоматически подставляет имя свойства, из которого был вызван метод, исключая ошибки из-за опечаток в строковых литералах.
  2. Сравнивайте значения: Всегда проверяйте, изменилось ли значение свойства, перед вызовом OnPropertyChanged(). Это предотвращает бесконечные циклы обновления и повышает производительность.
  3. Уведомление для вычисляемых свойств: Если свойство B зависит от свойства A, при изменении A нужно вызвать уведомление и для B.
  4. Библиотеки для упрощения: В больших проектах используйте CommunityToolkit.Mvvm (атрибут [ObservableProperty]) или Fody/PropertyChanged для автоматической генерации шаблонного кода.

Ответ 18+ 🔞

А, INotifyPropertyChanged, классика жанра! Ну, слушай сюда, это же та самая магия, которая заставляет твой интерфейс не отставать от логики, когда данные меняются. Без неё — пиздец, вручную всё обновлять, как в каменном веке.

Вот смотри, основа основ. Берёшь свой класс, например, для товара, и делаешь вот такую штуку:

using System.ComponentModel;
using System.Runtime.CompilerServices;

public class ProductViewModel : INotifyPropertyChanged
{
    private decimal _price;
    public decimal Price
    {
        get => _price;
        set
        {
            // Сначала проверь, а поменялось ли что-то вообще? Чтобы не дрочить систему попусту.
            if (_price == value) return;
            _price = value;
            // А вот тут кричи на всю деревню, что значение обновилось!
            OnPropertyChanged();
            // И заодно крикни, что свойство, которое от цены зависит, тоже надо пересчитать!
            OnPropertyChanged(nameof(PriceWithTax));
        }
    }

    // Вот это свойство считает цену с налогом. Оно зависит от Price.
    public decimal PriceWithTax => _price * 1.20m;

    // А это самое главное событие. На него подписывается интерфейс и ждёт, когда его позовут.
    public event PropertyChangedEventHandler? PropertyChanged;

    // И этот метод его зовёт. Красота!
    protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Теперь по дельным советам, чтобы не наступать на грабли:

  1. [CallerMemberName] — твой лучший друг. Этот атрибут — просто волшебство. Ты вызываешь OnPropertyChanged() без параметров, а он сам, как хитрая жопа, подставляет имя свойства, откуда его вызвали. Никаких опечаток в строках, ебать колотить!
  2. Сравнивай значения, блядь! Обязательно делай проверку if (_price == value) return;. Иначе будешь слать уведомления почём зря, производительность просядет, а в сложных связках можно и в бесконечный цикл угодить. Овердохуища проблем на ровном месте.
  3. Не забывай про вычисляемые свойства. Если у тебя есть свойство PriceWithTax, которое считается из Price, то при изменении Price надо крикнуть не только за себя, но и за него: OnPropertyChanged(nameof(PriceWithTax));. Иначе интерфейс так и будет показывать старую сумму с налогом, доверия к нему будет ноль ебать.
  4. Не изобретай велосипед на больших проектах. Писать эту лапшу для каждого свойства — терпения ебать ноль. Бери готовые инструменты: CommunityToolkit.Mvvm с его атрибутом [ObservableProperty] или Fody/PropertyChanged. Они эту рутину за тебя сгенерируют, и жить станет проще. Честно.

Вот и вся философия. Сделал правильно — и интерфейс летает, данные синхронны. Сделал криво — получаешь тормоза и баги, от которых волосы дыбом встают. Удачи!