2009-07-31 62 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编程时,诀窍是想“我希望我的框架提供什么事件?”然后去创建它。我们训练将事件视为简单的输入驱动事物(“鼠标悬停”,“鼠标点击”,“键盘”等)。然而,没有理由说明事件不会非常复杂且特定于您的应用(“GoogleMsdnMashupStockDataArrived”,“DragStarting”和“ImageContentChanged”)。当你用这种方式构建你的程序时(创建我需要的事件,然后通过改变状态来响应它),你会发现它们有更少的状态错误,变得更有序,并且更加自我描述。

Got it? :-)

3

它只是基于事件的编程模型的扩展。你创建了一些实现IObserver的东西,基本上你会说“这是我在集合中的某些东西发生变化时想要发生的事情”。这样,这只是我们在事件中所做的一切标准化工作。

与IEnumerable模式相比,它们正在推动它的发展。 IEnumerable是“拉”,而IObservable是“推”。

我看到的直接事件的唯一优点是它是一个标准化的接口。虽然(和INotifyCollectionChanged)在这里我看到了与ObservableCollection的重叠。也许他们试图在.NET中采用PERL座右铭:“有多种方法可以实现它”。

+4

斯科特接下来的问题是,将框架的样子,如果的IObservable和IObserver在框架的第1版实施了什么。 错误处理和事件编程本来就是可组合的。 处理异步和同步事件的常用方法。 并行扩展将使用IObservable类型。 但最重要的是,这将从一开始就全部组合在一起,如果现在不是几乎不可能的话,这将大大简化许多难以处理的项目。 (单元测试异步UI是我头脑中想到的一个) – DouglasH 2009-10-02 18:41:36

+1

@Doughlas,但我们不能重写历史,所以这个问题不是“theOldWay或Rx”,而是“theOldWay OR(theOldWay和Rx )“ – 2010-02-24 11:00:49

3

我不知道的优点,但我看到经典的.NET事件有以下区别:

错误通知

经典事件将需要为这个单独的事件,或EventArgs类需要检查的Error属性。

结束通知的通知

经典事件将需要为这个或与需要检查一个Final财产独立事件的EventArgs类。