Преимущества.NET Rx по классическим событиям?

3 ответа

Вы можете использовать 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 добавляет ценность. Без него мне пришлось бы использовать переменные состояния, чтобы события запускались в определенном порядке. Мне также пришлось бы написать довольно уродливый код для явного отсоединения от события LayoutUpdated.

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

Понятно? :-)

35
ответ дан 28 November 2019 в 03:25
поделиться

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

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

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

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

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

4
ответ дан 28 November 2019 в 03:25
поделиться

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

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

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

3
ответ дан 28 November 2019 в 03:25
поделиться
Другие вопросы по тегам:

Похожие вопросы: