Na obsah stránky

Lepší události v C#

Aleš Roubíček | | # permalink

Když se nedávno ptali Anderse Hejlsberga na to, co by nejradši v C# změnil nebo udělal jinak, kdyby mohl, odpověděl, že by zrušil události. Přesněji, že by je spojil s konceptem vlastností. Na první pohled jsou totiž vlastnosti a události celkem podobné, ale na druhý zjistíte, že události mají poměrně složitou syntaxi a vyžadují psát ne moc pěkný kód.

Dřív (C# 1.0) to asi o moc líp udělat nešlo, ale dnes tomuto přání můžeme plně vyhovět. Stačí události vystavovat jako vlastnosti typu IObservable<T>. Toto rozhraní je jednou z novinek BCL .net 4.0 a hlavně reaktivních extenzí (Rx.net).

Pokud píšete nový kód, zvažte užití IObservable<T> namísto událostí. Např. F# má události takto řešené nativně.

Ze starého na nový

Vezměme si ukázkovou deklaraci události v C#:

Dříve jsme museli definovat vlastní typ delegáta události:

public delegate void ChangedEventHandler(object sender, EventArgs e);

Pak vystavit událost tohoto typu:

public event ChangedEventHandler Changed;

A nakonec někde v kódu událost odpálit:

if (Changed != null)
  Changed(this, e);

Nehledě na podivné přetížení operátoru pro zavěšení ovladače události:

List.Changed += new ChangedEventHandler(ListChanged);

Nové verze jazyka a BCL nám přinesly mnohá usnadnění od EventHandler<TEventArgs>, přes automatického odvození typu delegáta až po lambda výrazy. To však nic nezměnilo na samotné koncepci událostí…

A teď si to tedy přepišme na sjednocený model vlastností a událostí s užitím IObservable<T>:

readonly Subject<Unit> changed = new Subject<Unit>();

public IObservable<Unit> Changed {
  get { return changed; }
}

V kódu událost vyvoláme následovně:

changed.OnNext(new Unit());

A ke zpracování události se přihlásíme takhle:

List.Changed.Subscribe(ListChanged);

Krom čistšího kódu (všimněte si absence defenzivního kódu a přetěžování operátorů, které na první pohled nemusí být každému jasné, co dělá) máme i spoustu dalších výhod, které přináší reaktivní programování.

PS. Třidy Subject<T> a Unit nejsou součástí BCL, ale zmiňovaných Rx.net. Do projektu si je přidáte pomocí nuget příkazu Install-Package Rx-Main.

Našli jste v článku chybu? Máte námět na reportáž? Založte mi ticket.