Question

.NET 4.0 bêta 2 a a présenté le IObservable et IObserver .

Quels sont les avantages par rapport aux événements .NET classiques? Cela ne résout-il pas le même problème?

Était-ce utile?

La solution

Vous pouvez utiliser IObservable en tant qu'événement, en remplaçant le code qui expose les événements avec des propriétés de type IObservable, mais ce n'est pas vraiment l'objectif.

Il y a deux choses importantes à comprendre à propos d'IObservable:

  1. Il unifie deux concepts que nous ne savions pas comment unifier auparavant : les opérations asynchrones (qui renvoient généralement une seule valeur) et les événements (qui durent indéfiniment).

  2. Il est composable . Contrairement aux événements CLR, IAsyncResult ou INotifyCollectionChanged, il nous permet de créer des événements spécifiques à partir d'événements généraux et d'opérations asynchrones.

Voici un exemple que j'ai rencontré au travail cet après-midi.

Dans Silverlight, certains effets que vous pouvez appliquer à un contrôle image ne peuvent pas être appliqués à un contrôle normal. Pour contourner ces limitations lorsque le contenu d'un contrôle est modifié, je peux attendre que son apparence visuelle soit mise à jour et prendre une capture d'écran de celui-ci. Ensuite, je veux masquer sa représentation visuelle, la remplacer par l'instantané et appliquer les effets visuels à l'image. Maintenant, je peux appliquer des effets d'image à un contrôle (en supposant qu'il ne soit pas interactif).

Ce programme serait trivial si ce n’était le fait qu’il devait être asynchrone. Je dois attendre la fin de deux opérations asynchrones consécutives avant d'appliquer des effets à l'image:

  1. Le contenu du contrôle est modifié
  2. l'apparence visuelle du contrôle est mise à jour

Voici comment résoudre ce problème avec Rx:

// A content control is a control that displays content.  That content can be
// anything at all like a string or another control.  Every content control contains
// another control: a ContentPresenter.  The ContentPresenter's job is to generate
// a visual representation of the Content property. For example, if the Content property
// of the ContentControl is a string, the ContentPresenter creates a TextBlock and inserts
// the string into it.  On the other hand if the Content property is another control the 
// ContentPresenter just inserts it into the visual tree directly.
public class MyContentControl : ContentControl
{
   // A subject implements both IObservable and IObserver.  When IObserver methods
   // are called, it forwards those calls to all of its listeners.
   // As a result it has roughly the same semantics as an event that we can "raise."
   private Subject<object> contentChanged = new Subject<object>();

   // This is a reference to the ContentPresenter in the ContentControl's template
   private ContentPresenter contentPresenter; 

   // This is a reference to the Image control within ContentControl's template.  It is displayed on top of the ContentPresenter and has a cool blur effect applied to it.
   private Image contentImageControl; 

   public MyContentControl()
   {
      // Using Rx we can create specific events from general events.
      // In this case I want to create a specific event ("contentImageChanged") which
      // gives me exactly the data I need to respond and update the UI.
      var contentImageChanged = 
         // get the content from the content changed event
         from content in contentChanged
         where content != null
         // Wait for the ContentPresenter's visual representation to update.
         // ContentPresenter is data bound to the Content property, so it will
         // update momentarily.
         from _ in contentPresenter.GetLayoutUpdated().Take(1)
         select new WritableBitmap(contentPresenter, new TranslateTransform());

      contentImageChanged.Subscribe(
         contentImage => 
         {
            // Hide the content presenter now that we've taken a screen shot              
            contentPresenter.Visibility = Visibility.Collapsed; 

            // Set the image source of the image control to the snapshot
            contentImageControl.ImageSource = contentImage;
         });
   }

   // This method is invoked when the Content property is changed.
   protected override OnContentChanged(object oldContent, object newContent)
   {
      // show the content presenter before taking screenshot
      contentPresenter.Visibility = Visibility.Visible;  

      // raise the content changed "event"
      contentChanged.OnNext(newContent);   

      base.OnContentChanged(oldContent, newContent);
   }
}

Cet exemple est particulièrement simple dans la mesure où il n’ya que deux opérations consécutives à séquencer. Même dans cet exemple simple, nous pouvons voir que Rx ajoute de la valeur. Sans cela, j'aurais dû utiliser des variables d'état pour s'assurer que les événements se déclenchaient dans un certain ordre. J'aurais aussi dû écrire un code assez laid pour se détacher explicitement de l'événement LayoutUpdated.

Lorsque vous programmez avec Rx, le truc est de penser "Quel événement est-ce que je souhaite que mon framework soit fourni?" et puis allez le créer. Nous sommes formés pour considérer les événements comme des choses simples et axées sur les entrées ("souris au survol", "clic de souris", "touche au clavier", etc.). Toutefois, il n'y a aucune raison que des événements ne puissent pas être très complexes et spécifiques à votre application ("GoogleMsdnMashupStockDataArrived", "DragStarting" et "ImageContentChanged"). Lorsque vous structurez vos programmes de cette manière (créez exactement l'événement dont vous avez besoin , puis en y changeant d'état), vous constaterez qu'ils ont moins de bugs d'état, qu'ils sont mieux ordonnés et qu'ils sont plus autonomes. -décrivant.

Compris? :-)

Autres conseils

Je ne suis pas sûr des avantages, mais je vois les différences suivantes avec les événements .NET classiques:

notifications d'erreur

Les événements

classiques nécessiteraient un événement distinct pour cela ou une classe EventArgs avec une propriété Error à vérifier.

notification de fin de notification

Les événements classiques nécessiteraient un événement distinct pour cela ou une classe EventArgs avec une propriété Final à vérifier.

Il s’agit simplement d’une extension du modèle de programmation par événement. Vous créez quelque chose qui implémente IObserver et, fondamentalement, vous dites "voici ce que je veux qu'il se passe lorsque quelque chose dans la collection change". De cette façon, il s’agit simplement d’une normalisation de ce que nous avons tous fait avec les événements.

Ils le poussent comme si c'était un gros problème par rapport au modèle IEnumerable. IEnumerable est "pull", alors que IObservable est "push".

Le seul avantage que je vois par rapport aux événements consécutifs est qu’il s’agit d’une interface normalisée. Je vois cependant un grand chevauchement avec ObservableCollection (et INotifyCollectionChanged). Ils essaient peut-être d’adopter la devise PERL avec .NET: "Il existe plus d’une façon de le faire".

Vous devriez absolument regarder Rx Workshop: Observables versus Events vidéo et compléter le défi ci-joint

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top