Lepší události v C#
|
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
.