Pontage avec des sources asynchrones existantes
Outre les événements .NET, d’autres sources de données asynchrones existent dans le .NET Framework. L’une d’elles est le modèle de méthode asynchrone. Dans ce modèle de conception, deux méthodes sont fournies. Une méthode (généralement nommée BeginX) est utilisée pour démarrer le calcul et retourne un handle IAsyncResult qui est passé à la deuxième méthode (généralement nommée EndX), qui récupère ensuite le résultat du calcul. L’achèvement est généralement signalé en implémentant un délégué AsyncCallback ou en interrogeant IAsyncResult.IsCompleted. Le code qui adhère à ce modèle est souvent difficile à lire et à gérer. Dans cette rubrique, nous allons montrer comment utiliser des méthodes de fabrique Rx pour convertir ces sources de données asynchrones en séquences observables.
Conversion de modèles asynchrones en séquences observables
De nombreuses méthodes asynchrones dans .NET sont écrites avec des signatures telles que BeginX et EndX où X est le nom de la méthode en cours d’exécution de façon asynchrone. BeginX prend des arguments pour exécuter la méthode, un AsyncCallback qui est une action qui prend un IAsyncResult et ne retourne rien, et enfin un état d’objet. EndX prend l’IAsyncResult qui est transmis à partir de l’AsyncCallback pour récupérer la valeur de l’appel asynchrone.
L’opérateur FromAsyncPattern du type Observable encapsule les méthodes Begin et End (qui sont passées en tant que paramètres à l’opérateur) et retourne une fonction qui prend les mêmes paramètres que Begin et retourne une observable. Cette observable représente une séquence qui publie une valeur unique, qui est le résultat asynchrone de l’appel que vous venez de spécifier.
Dans l’exemple suivant, nous allons convertir BeginRead et EndRead pour un objet Stream qui utilise le modèle IAsyncResult en fonction qui retourne une séquence observable. Pour les paramètres génériques de l’opérateur FromAsyncPattern, nous spécifions les types des arguments de BeginRead jusqu’au rappel. Étant donné que la méthode EndRead retourne une valeur, nous ajoutons ce type comme paramètre générique final pour FromAsyncPattern. Si vous pointez sur var
pour read
, vous remarquerez que la valeur de retour de FromAsyncPattern est un délégué de fonction qui a la signature suivante : Func<byte[], int32,int32, IObservable<int32>>
, ce qui signifie que cette fonction prend 3 paramètres (les mêmes pour BeginRead) et retourne un IObservable<Int32>. Cet objet IObservable contient une valeur, qui est l’entier retourné par EndRead, et contient le nombre d’octets lus à partir du flux, entre zéro (0) et le nombre d’octets demandés. Étant donné que nous obtenons maintenant un IObservable au lieu d’un IAsyncResult, nous pouvons utiliser tous les opérateurs LINQ disponibles pour Observables et l’abonner, l’analyser ou le composer.
Stream inputStream = Console.OpenStandardInput();
var read = Observable.FromAsyncPattern<byte[], int, int, int>(inputStream.BeginRead, inputStream.EndRead);
byte[] someBytes = new byte[10];
IObservable<int> source = read(someBytes, 0, 10);
IDisposable subscription = source.Subscribe(
x => Console.WriteLine("OnNext: {0}", x),
ex => Console.WriteLine("OnError: {0}", ex.Message),
() => Console.WriteLine("OnCompleted"));
Console.ReadKey();