Преимущества .NET Rx перед классическими событиями? - PullRequest
28 голосов
/ 31 июля 2009

.NET 4.0 beta 2 имеет введенные интерфейсы IObservable и IObserver .

Каковы преимущества по сравнению с классическими событиями .NET? Разве это не решает ту же проблему?

Ответы [ 4 ]

35 голосов
/ 08 августа 2009

Вы можете использовать IObservable как событие, заменив код, который предоставляет события свойствами типа IObservable, но это не совсем так.

В IObservable есть две важные вещи:

  1. Он объединяет две концепции, которые мы не знали, как объединять до : асинхронные операции (которые обычно возвращают одно значение) и события (которые обычно продолжаются вечно).

  2. Это составно . В отличие от событий CLR, IAsyncResult или INotifyCollectionChanged, он позволяет нам создавать конкретные события из общих событий и асинхронных операций.

Вот пример, с которым я столкнулся на работе сегодня днем.

В Silverlight есть некоторые эффекты, которые вы можете применить к элементу управления изображением, которые нельзя применить к обычному элементу управления. Чтобы обойти эти ограничения при изменении содержимого элемента управления, я могу дождаться обновления его внешнего вида и сделать снимок экрана. Затем я хочу скрыть его визуальное представление, заменить его снимком и применить визуальные эффекты к изображению. Теперь я могу применять эффекты изображения к элементу управления (при условии, что он не интерактивный).

Эта программа будет тривиальной, но она должна быть асинхронной. Я должен дождаться завершения двух последовательных асинхронных операций, прежде чем смогу применить эффекты к изображению:

  1. Содержание элемента управления изменено
  2. Обновлен внешний вид элемента управления

Вот как я могу решить эту проблему, используя 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);
   }
}

Этот пример особенно прост, учитывая, что для последовательности требуется всего две последовательные операции. Даже в этом простом примере мы видим, что Rx добавляет ценность. Без этого мне пришлось бы использовать переменные состояния, чтобы события запускались в определенном порядке. Я также должен был бы написать довольно уродливый код для отсоединения explicity от события LayoutUpdated.

Когда вы программируете с помощью Rx, уловка заключается в том, чтобы подумать: «Какое событие я хотел бы предоставить моей платформе?» а затем иди создать его. Мы обучены думать о событиях как о простых, управляемых вводом вещах («наведение мыши», «щелчок мышью», «нажатие клавиш» и т. Д.). Однако нет причин, по которым события не могут быть очень сложными и специфичными для вашего приложения («GoogleMsdnMashupStockDataArrived», «DragStarting» и «ImageContentChanged»). Когда вы структурируете свои программы таким образом (создаете именно то событие, которое мне нужно , а затем реагируют на него, изменяя состояние), вы обнаружите, что они имеют меньше ошибок состояния, становятся более упорядоченными и в целом более самостоятельными описания.

Понял? :-)

4 голосов
/ 03 августа 2009

Я не уверен в преимуществах, но вижу следующие отличия от классических событий .NET:

уведомления об ошибках

Классические события потребуют отдельного события для этого или класса EventArgs со свойством Error, которое необходимо проверить.

уведомление об окончании уведомлений

Классические события потребуют отдельного события для этого или EventArgs класса со свойством Final, которое необходимо проверить.

3 голосов
/ 31 июля 2009

Это просто расширение модели программирования, основанной на событиях. Вы создаете что-то, что реализует IObserver, и в основном вы говорите: «Вот что я хочу, чтобы что-то изменилось в коллекции». Таким образом, это просто стандартизация того, что мы все делаем с событиями.

Они выдвигают это, как будто это большое поворотное лицо по сравнению с шаблоном IEnumerable. IEnumerable - это «pull», а IObservable - «push».

Единственное преимущество, которое я вижу перед прямыми событиями, это то, что это стандартизированный интерфейс. Я вижу большое перекрытие с ObservableCollection здесь, хотя (и INotifyCollectionChanged). Возможно, они пытаются перенять девиз PERL в .NET: «есть несколько способов сделать это».

2 голосов
/ 18 октября 2011

Вы обязательно должны посмотреть Rx Workshop: Observables против событий видео и завершить прилагаемый вызов

...