Implementace asynchronního vzoru založeného na událostech

Pokud píšete třídu s některými operacemi, které můžou způsobovat znatelné zpoždění, zvažte poskytnutí asynchronní funkce implementací asynchronního vzoru založeného na událostech.

Asynchronní vzor založený na událostech poskytuje standardizovaný způsob balení třídy, která má asynchronní funkce. Pokud je implementováno s pomocnými třídami, jako je AsyncOperationManager, vaše třída bude fungovat správně v libovolném aplikačním modelu, včetně ASP.NET, konzolových aplikací a model Windows Forms aplikací.

Příklad, který implementuje asynchronní vzor založený na událostech, viz Postupy: Implementace komponenty, která podporuje asynchronní vzor založený na událostech.

U jednoduchých asynchronních operací můžete najít vhodný komponentu BackgroundWorker . Další informace o BackgroundWorkertom, viz Postupy: Spuštění operace na pozadí.

Následující seznam popisuje funkce asynchronního vzoru založeného na událostech popsaných v tomto tématu.

  • Příležitosti k implementaci asynchronního vzoru založeného na událostech

  • Pojmenování asynchronních metod

  • Volitelně můžete podporovat zrušení.

  • Volitelně podporuje vlastnost IsBusy

  • Volitelně můžete poskytnout podporu pro vykazování průběhu.

  • Volitelně můžete poskytnout podporu pro vracení přírůstkových výsledků.

  • Zpracování parametrů out a ref v metodách

Příležitosti k implementaci asynchronního vzoru založeného na událostech

Zvažte implementaci asynchronního vzoru založeného na událostech v následujících případech:

  • Klienti vaší třídy nepotřebují WaitHandle a IAsyncResult objekty dostupné pro asynchronní operace, což znamená, že dotazování a WaitAll nebo WaitAny bude nutné sestavit klientem.

  • Chcete, aby klient spravil asynchronní operace se známým modelem událostí nebo delegáta.

Každá operace je kandidátem na asynchronní implementaci, ale ty, u kterých očekáváte, že se budou brát v úvahu dlouhé latence. Zvláště vhodné jsou operace, ve kterých klienti volají metodu a jsou upozorněni na dokončení bez nutnosti dalšího zásahu. Vhodné jsou také operace, které běží nepřetržitě, pravidelně upozorňují klienty na průběh, přírůstkové výsledky nebo změny stavu.

Další informace o rozhodování o tom, kdy podporovat asynchronní vzor založený na událostech, naleznete v tématu Rozhodování o implementaci asynchronního vzoru založeného na událostech.

Pojmenování asynchronních metod

Pro každou synchronní metodu MethodName , pro kterou chcete poskytnout asynchronní protějšek:

Definujte metodu MethodNameAsync , která:

  • Vrací objekt void.

  • Vezme stejné parametry jako MethodName metoda.

  • Přijímá více vyvolání.

Volitelně definujte přetížení MethodNameAsync , shodné s MethodNameAsync, ale s dalším parametrem s hodnotou objektu volaným userState. Pokud jste připraveni spravovat více souběžných vyvolání metody, v takovém případě userState se hodnota doručí zpět do všech obslužných rutin událostí k rozlišení vyvolání metody. Můžete se také rozhodnout, že to uděláte jednoduše jako místo pro uložení stavu uživatele pro pozdější načtení.

Pro každý samostatný podpis metody MethodNameAsync :

  1. Definujte následující událost ve stejné třídě jako metoda:

    Public Event MethodNameCompleted As MethodNameCompletedEventHandler
    
    public event MethodNameCompletedEventHandler MethodNameCompleted;
    
  2. Definujte následující delegáta a AsyncCompletedEventArgs. Ty budou pravděpodobně definovány mimo samotnou třídu, ale ve stejném oboru názvů.

    Public Delegate Sub MethodNameCompletedEventHandler( _
        ByVal sender As Object, _
        ByVal e As MethodNameCompletedEventArgs)
    
    Public Class MethodNameCompletedEventArgs
        Inherits System.ComponentModel.AsyncCompletedEventArgs
    Public ReadOnly Property Result() As MyReturnType
    End Property
    
    public delegate void MethodNameCompletedEventHandler(object sender,
        MethodNameCompletedEventArgs e);
    
    public class MethodNameCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs
    {
        public MyReturnType Result { get; }
    }
    
    • Ujistěte se, že MethodNameCompletedEventArgs třída zveřejňuje své členy jako vlastnosti jen pro čtení, a ne pole, jako pole brání datové vazbě.

    • Nedefinujte žádné AsyncCompletedEventArgs-odvozené třídy pro metody, které nevytvářejí výsledky. Stačí použít samotnou instanci AsyncCompletedEventArgs .

      Poznámka:

      Je naprosto přijatelné, pokud je to možné a vhodné, opakovaně používat delegáta a AsyncCompletedEventArgs typy. V tomto případě nebude pojmenování tak konzistentní s názvem metody, protože daný delegát a AsyncCompletedEventArgs nebude svázán s jedinou metodou.

Volitelně můžete podporovat zrušení.

Pokud vaše třída bude podporovat zrušení asynchronních operací, zrušení by mělo být vystaveno klientovi, jak je popsáno níže. Před definováním podpory zrušení je potřeba dosáhnout dvou rozhodovacích bodů:

  • Má vaše třída, včetně budoucích očekávaných přidání, pouze jednu asynchronní operaci, která podporuje zrušení?
  • Mohou asynchronní operace, které podporují zrušení, podporovat více čekajících operací? To znamená, že metoda MethodNameAsync používá userState parametr a umožňuje několik vyvolání před čekáním na dokončení?

Pomocí odpovědí na tyto dvě otázky v tabulce níže určete, jaký podpis pro metodu zrušení má být.

Visual Basic

Podporováno více souběžných operací Pouze jedna operace najednou
Jedna asynchronní operace v celé třídě Sub MethodNameAsyncCancel(ByVal userState As Object) Sub MethodNameAsyncCancel()
Více asynchronních operací ve třídě Sub CancelAsync(ByVal userState As Object) Sub CancelAsync()

C#

Podporováno více souběžných operací Pouze jedna operace najednou
Jedna asynchronní operace v celé třídě void MethodNameAsyncCancel(object userState); void MethodNameAsyncCancel();
Více asynchronních operací ve třídě void CancelAsync(object userState); void CancelAsync();

Pokud definujete metodu CancelAsync(object userState) , klienti musí být opatrní při výběru jejich stavových hodnot, aby je mohli rozlišit mezi všemi asynchronními metodami vyvolanými na objektu, a ne pouze mezi všemi vyvoláním jedné asynchronní metody.

Rozhodnutí o pojmenování verze MethodNameAsyncCancel s jednou asynchronní operací je založené na tom, že je možné snadněji zjistit metodu v návrhovém prostředí, jako je IntelliSense sady Visual Studio. Tím se seskupí související členy a odlišují je od ostatních členů, které nemají nic společného s asynchronními funkcemi. Pokud očekáváte, že v následujících verzích mohou být přidány další asynchronní operace, je lepší definovat CancelAsync.

Ve stejné třídě nedefinujte více metod z výše uvedené tabulky. To nebude dávat smysl, nebo to bude nepotřebné rozhraní třídy s šířením metod.

Tyto metody se obvykle vrátí okamžitě a operace může nebo nemusí být ve skutečnosti zrušena. V obslužné rutině události MethodNameCompleted událost, MethodNameCompletedEventArgs objekt obsahuje Cancelled pole, které klienti mohou použít k určení, zda došlo ke zrušení.

Dodržujte sémantiku zrušení popsanou v osvědčených postupech pro implementaci asynchronního vzoru založeného na událostech.

Volitelně podporuje vlastnost IsBusy

Pokud vaše třída nepodporuje více souběžných vyvolání, zvažte zveřejnění IsBusy vlastnosti. Vývojáři tak můžou určit, jestli je spuštěná metoda MethodNameAsync, aniž by zachytili výjimku z metody MethodNameAsync.

Dodržujte sémantiku IsBusy popsanou v osvědčených postupech pro implementaci asynchronního vzoru založeného na událostech.

Volitelně můžete poskytnout podporu pro vykazování průběhu.

Často je žádoucí, aby asynchronní operace hlásila průběh během své operace. Asynchronní vzor založený na událostech poskytuje vodítko k tomu.

  • Volitelně definujte událost, která má být vyvolána asynchronní operací a vyvolána v příslušném vlákně. Objekt ProgressChangedEventArgs nese celočíselnou hodnotu indikátor průběhu, který má být mezi 0 a 100.

  • Tuto událost pojmenujte následujícím způsobem:

    • ProgressChanged pokud má třída více asynchronních operací (nebo se očekává, že bude růst tak, aby zahrnovala více asynchronních operací v budoucích verzích);

    • MethodNameProgressChanged , pokud třída má jednu asynchronní operaci.

    Tato volba pojmenování paralelně, která byla provedena pro metodu zrušení, jak je popsáno v části Volitelné podpora zrušení.

Tato událost by měla používat podpis delegáta ProgressChangedEventHandlerProgressChangedEventArgs a třídu. Případně pokud je možné poskytnout indikátor průběhu specifický pro doménu (například bajty přečtené a celkové bajty pro operaci stahování), měli byste definovat odvozenou třídu ProgressChangedEventArgs.

Všimněte si, že pro třídu existuje pouze jedna ProgressChanged nebo MethodNameProgressChanged událost bez ohledu na počet asynchronních metod, které podporuje. Očekává se, že klienti budou používat userState objekt předaný metodám MethodNameAsync k rozlišení mezi probíhajícími aktualizacemi v několika souběžných operacích.

Může docházet k situacím, kdy více operací podporuje průběh a každý z nich vrací jiný ukazatel průběhu. V takovém případě není jedna ProgressChanged událost vhodná a můžete zvážit podporu více ProgressChanged událostí. V tomto případě použijte vzor pojmenování MethodNameProgressChanged pro každou metodu MethodNameAsync.

Dodržujte sémantiku generování sestav průběhu popsanou v osvědčených postupech pro implementaci asynchronního vzoru založeného na událostech.

Volitelně můžete poskytnout podporu pro vracení přírůstkových výsledků.

Někdy může asynchronní operace vrátit přírůstkové výsledky před dokončením. Pro podporu tohoto scénáře je možné použít řadu možností. Následují některé příklady.

Třída single-operation

Pokud vaše třída podporuje pouze jednu asynchronní operaci a tato operace dokáže vrátit přírůstkové výsledky, pak:

  • ProgressChangedEventArgs Rozšiřte typ tak, aby přenášel data přírůstkových výsledků, a definujte událost MethodNameProgressChanged s tímto rozšířenými daty.

  • Vyvolání této události MethodNameProgressChanged , pokud je k dispozici přírůstkový výsledek pro sestavu.

Toto řešení se vztahuje konkrétně na třídu single-async-operation, protože neexistuje žádný problém se stejnou událostí, ke které dochází k vrácení přírůstkových výsledků pro "všechny operace", jak to dělá MethodNameProgressChanged událost.

Třída více operací s homogenními přírůstkovými výsledky

V tomto případě vaše třída podporuje více asynchronních metod, z nichž každá dokáže vracet přírůstkové výsledky a všechny tyto přírůstkové výsledky mají stejný typ dat.

Postupujte podle výše uvedeného modelu pro třídy s jednou operací, protože stejná EventArgs struktura bude fungovat pro všechny přírůstkové výsledky. ProgressChanged Definujte událost místo události MethodNameProgressChanged, protože se vztahuje na více asynchronních metod.

Třída více operací s heterogenními přírůstkovými výsledky

Pokud vaše třída podporuje více asynchronních metod, každá vrací jiný typ dat, měli byste:

  • Oddělte protokolování přírůstkových výsledků od generování sestav průběhu.

  • Definujte samostatnou událost MethodNameProgressChanged s odpovídající EventArgs pro každou asynchronní metodu pro zpracování dat přírůstkových výsledků této metody.

Vyvolání obslužné rutiny události na příslušném vlákně, jak je popsáno v osvědčených postupech pro implementaci asynchronního vzoru založeného na událostech.

Zpracování parametrů out a ref v metodách

I když se obecně nedoporučuje používat out rozhraní ref .NET, tady jsou pravidla, která se mají dodržovat, když jsou k dispozici:

Vzhledem k synchronní metodě MethodName:

  • outParametr methodName by neměl být součástí metody MethodNameAsync. Místo toho by měly být součástí MethodNameCompletedEventArgs se stejným názvem jako jeho ekvivalent parametru v MethodName (pokud neexistuje vhodnější název).

  • refParametry methodName by se měly zobrazovat jako součást MethodNameAsync a jako součást MethodNameCompletedEventArgs se stejným názvem jako jeho ekvivalent parametru v MethodName (pokud neexistuje vhodnější název).

Například:

Public Function MethodName(ByVal arg1 As String, ByRef arg2 As String, ByRef arg3 As String) As Integer
public int MethodName(string arg1, ref string arg2, out string arg3);

Asynchronní metoda a její AsyncCompletedEventArgs třída by vypadaly takto:

Public Sub MethodNameAsync(ByVal arg1 As String, ByVal arg2 As String)

Public Class MethodNameCompletedEventArgs
    Inherits System.ComponentModel.AsyncCompletedEventArgs
    Public ReadOnly Property Result() As Integer
    End Property
    Public ReadOnly Property Arg2() As String
    End Property
    Public ReadOnly Property Arg3() As String
    End Property
End Class
public void MethodNameAsync(string arg1, string arg2);

public class MethodNameCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs
{
    public int Result { get; };
    public string Arg2 { get; };
    public string Arg3 { get; };
}

Viz také