ObservableObject

ObservableObject je základní třída pro objekty, které lze pozorovat implementací rozhraní INotifyPropertyChanged a INotifyPropertyChanging. Dá se použít jako výchozí bod pro všechny druhy objektů, které potřebují podporovat oznámení o změnách vlastností.

Rozhraní API platformy:ObservableObject, TaskNotifierTaskNotifier<T>

Jak to funguje

ObservableObject má následující hlavní funkce:

  • Poskytuje základní implementaci pro INotifyPropertyChanged a INotifyPropertyChanging a zpřístupňuje události PropertyChanged a PropertyChanging.
  • Poskytuje řadu SetProperty metod, které lze použít ke snadnému nastavování hodnot vlastností u typů odvozených od ObservableObject a k automatickému vyvolávání příslušných událostí.
  • Poskytuje metodu SetPropertyAndNotifyOnCompletion , která je analogická SetProperty , ale s možností nastavit Task vlastnosti a vyvolat události oznámení automaticky při dokončení přiřazených úkolů.
  • Zpřístupňuje metody OnPropertyChanged a OnPropertyChanging, které lze v odvozených typech přepsat a přizpůsobit tak způsob vyvolávání oznamovacích událostí.

Jednoduchá vlastnost

Tady je příklad implementace podpory oznámení pro vlastní vlastnost:

public class User : ObservableObject
{
    private string name;

    public string Name
    {
        get => name;
        set => SetProperty(ref name, value);
    }
}

Zadaná SetProperty<T>(ref T, T, string) metoda zkontroluje aktuální hodnotu vlastnosti a aktualizuje ji, pokud se liší, a pak také vyvolá příslušné události automaticky. Název vlastnosti se automaticky zaznamenává pomocí atributu [CallerMemberName] , takže není nutné ručně určit, která vlastnost se aktualizuje.

Zabalení nepozorovatelného modelu

Běžným scénářem například při práci s databázovými položkami je vytvoření obalového modelu podporujícího datovou vazbu, který zpřístupňuje vlastnosti databázového modelu a v případě potřeby vyvolává oznámení o změně vlastnosti. To je potřeba také v případě, že chcete do modelů vložit podporu oznámení, která rozhraní neimplementují INotifyPropertyChanged . ObservableObject poskytuje vyhrazenou metodu, která usnadňuje tento proces. V následujícím příkladu User je model přímo mapující databázovou tabulku bez dědění z ObservableObject:

public class ObservableUser : ObservableObject
{
    private readonly User user;

    public ObservableUser(User user) => this.user = user;

    public string Name
    {
        get => user.Name;
        set => SetProperty(user.Name, value, user, (u, n) => u.Name = n);
    }
}

V tomto případě používáme přetížení SetProperty<TModel, T>(T, T, TModel, Action<TModel, T>, string). Podpis je o něco složitější než předchozí – to je nezbytné k tomu, aby byl kód stále velmi efektivní, i když nemáme přístup k záložnímu poli, jako je v předchozím scénáři. Jednotlivé části tohoto podpisu metody si můžeme podrobně projít, abychom lépe porozuměli funkci jednotlivých částí:

  • TModel je argument typu označující typ modelu, který zabalíme. V tomto případě to bude třída User. Všimněte si, že toto explicitní zadání nepotřebujeme – kompilátor jazyka C# to automaticky odvodí tím, jak metodu SetProperty vyvoláváme.
  • T je typ vlastnosti, kterou chceme nastavit. TModelPodobně jako , to je odvozeno automaticky.
  • T oldValue je první parametr a v tomto případě používáme user.Name k předání aktuální hodnoty této vlastnosti, kterou zabalíme.
  • T newValue je nová hodnota nastavená na vlastnost a zde předáváme value, což je vstupní hodnota v rámci setter vlastnosti.
  • TModel model je cílový model, který zabalíme, v tomto případě předáváme instanci uloženou user v poli.
  • Action<TModel, T> callback je funkce, která se vyvolá, pokud se nová hodnota vlastnosti liší od aktuální hodnoty a vlastnost musí být nastavena. To provede tato funkce zpětného volání, která přijímá jako vstup cílový model a novou hodnotu vlastnosti, která se má nastavit. V tomto případě pouze přiřazujeme vstupní hodnotu (kterou jsme nazvali n) vlastnosti Name (provedením u.Name = n). Je důležité se zde vyhnout zachycení hodnot z aktuálního oboru a pracovat pouze s hodnotami zadanými jako vstup pro zpětné volání, protože to umožňuje kompilátoru jazyka C# ukládat funkci zpětného volání do mezipaměti a provádět řadu vylepšení výkonu. Právě proto zde nepřistupujeme přímo k poli user ani k parametru value v setteru, ale místo toho používáme pouze vstupní parametry lambda výrazu.

Metoda SetProperty<TModel, T>(T, T, TModel, Action<TModel, T>, string) výrazně usnadňuje vytváření těchto obalových vlastností, protože zajišťuje načítání i nastavování cílových vlastností a zároveň nabízí velmi kompaktní rozhraní API.

Poznámka:

Ve srovnání s implementací této metody pomocí výrazů LINQ, konkrétně prostřednictvím parametru typu Expression<Func<T>> místo parametrů stavu a zpětného volání, zlepšení výkonu, které lze dosáhnout tímto způsobem, jsou opravdu významné. Konkrétně je tato verze ~200x rychlejší než ta, která používá výrazy LINQ, a neuvádí vůbec žádné přidělení paměti.

Práce s Task<T> vlastnostmi

Je-li vlastnost Task také nutné vyvolat událost oznámení po dokončení úkolu, aby byly vazby aktualizovány ve správný čas. Např. zobrazení indikátoru načítání nebo jiné informace o stavu operace reprezentované úkolem. ObservableObject má rozhraní API pro tento scénář:

public class MyModel : ObservableObject
{
    private TaskNotifier<int>? requestTask;

    public Task<int>? RequestTask
    {
        get => requestTask;
        set => SetPropertyAndNotifyOnCompletion(ref requestTask, value);
    }

    public void RequestValue()
    {
        RequestTask = WebService.LoadMyValueAsync();
    }
}

SetPropertyAndNotifyOnCompletion<T>(ref TaskNotifier<T>, Task<T>, string) Zde se metoda postará o aktualizaci cílového pole, monitorování nového úkolu, pokud je k dispozici, a vyvolání události oznámení po dokončení úkolu. Tímto způsobem je možné vytvořit vazbu na vlastnost úkolu a upozornit na změny stavu. TaskNotifier<T> je speciální typ zpřístupněný pomocí ObservableObject, který obaluje cílovou instanci Task<T> a zajišťuje potřebnou logiku upozorňování pro tuto metodu. Typ TaskNotifier lze také použít přímo, pokud máte pouze obecný Task.

Poznámka:

Metoda SetPropertyAndNotifyOnCompletion je určena k nahrazení použití NotifyTaskCompletion<T> typu z Microsoft.Toolkit balíčku. Pokud se tento typ používal, lze jej nahradit pouze vnitřní vlastností Task (nebo Task<TResult>) a poté lze metodu SetPropertyAndNotifyOnCompletion použít k nastavení její hodnoty a vyvolání oznámení o změně. Všechny vlastnosti vystavené typem NotifyTaskCompletion<T> jsou k dispozici přímo na Task instancích.

Příklady