2009-07-31 9 views

ответ

33

Вы можете использовать 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, трюк состоит в том, чтобы подумать: «Какое событие я хочу, чтобы моя инфраструктура была предоставлена?» а затем создайте его. Мы подготовлены к тому, чтобы думать о событиях как о простых, управляемых событиями вещах («mouseover», «mouseclick», «keyup» и т. Д.). Однако нет причин, по которым события не могут быть очень сложными и специфичными для вашего приложения («GoogleMsdnMashupStockDataArrived», «DragStarting» и «ImageContentChanged»). Когда вы структурируете свои программы таким образом (создайте именно то, что мне нужно , а затем ответьте на это, изменив состояние), вы обнаружите, что у них меньше ошибок состояния, становятся более упорядоченными и в целом более самоописаны.

Есть? :-)

+0

greate example (отсюда +1), однако я думаю, что я предпочел бы видеть машину состояний, поскольку это будет долгое время, прежде чем все программисты поймут Rx. Я не могу решить, что у пост-программистов нет степени Comp Sci, если Rx - это шаг слишком далеко ... –

3

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

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

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

+4

Скотт вопрос тогда, что бы рамка выглядеть, если IObservable и IObserver были реализованы в версии 1 каркаса. Обработка ошибок и программирование событий были бы составными. Обычный способ обработки асинхронных и синхронизирующих событий. Параллельные расширения использовали бы типы IObservable. Но главное, что с самого начала все это было бы скомбинировано, что значительно упростило бы множество предметов, которые сейчас трудны, если не совсем невозможно. (Unit testing Асинхронный пользовательский интерфейс - это тот, который приходит в голову с моей головы) – DouglasH

+1

@Doughlas, но мы не можем переписать историю, поэтому quistion не является «OldWay или Rx», а скорее «OldWay OR (theOldWay и Rx) « –

+0

Основным преимуществом IObservable является его композиционная способность. Поскольку у вас есть большая коллекция встроенных им операторов, которые полагаются на интерфейс IObservable , вы можете очень легко создавать очень сложные взаимодействия. С необработанными событиями вам придется прибегать к тому, чтобы иметь много состояний, чтобы отслеживать все вызовы событий. – ionoy

3

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

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

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

истекшим уведомление уведомления

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