.NET Rx优于经典事件的优势?

时间:2009-07-31 13:26:45

标签: .net events system.reactive reactive-programming

.NET 4.0 beta 2introducedIObservable接口IObserver

与传统.NET事件相比有哪些优势?这不能解决同样的问题吗?

4 个答案:

答案 0 :(得分:35)

您可以将IObservable用作事件,替换使用IObservable类型的属性公开事件的代码,但这不是重点。

有关IObservable的两个重要事项:

  1. 它统一了两个我们不知道如何在之前统一的概念:异步操作(通常返回单个值)和事件(通常会永远持续)。

  2. 可组合。与CLR事件,IAsyncResult或INotifyCollectionChanged不同,它允许我们从一般事件和异步操作中构建特定事件。

  3. 这是我今天下午在工作中遇到的一个例子。

    在Silverlight中,有一些效果可以应用于无法应用于普通控件的图像控件。为了解决控件内容更改时的这些限制,我可以等待其视觉外观更新并截取它的屏幕截图。然后我想隐藏其可视化表示,用快照替换它,并将视觉效果应用于图像。现在我可以将图像效果应用于控件(假设它不是交互式的)。

    这个程序很简单,但它必须是异步的。在将效果应用于图像之前,我必须等待两个连续的异步操作才能完成:

    1. 控件的内容已更改
    2. 控件的视觉外观已更新
    3. 以下是我如何使用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”)。当你以这种方式构建你的程序时(完全创建我需要的事件然后通过改变状态来响应它)你会发现他们有更少的状态错误,变得更有序,并且更加自我-describing。

      知道了吗? :-)

答案 1 :(得分:4)

我不确定这些优点,但我发现以下与经典.NET事件的区别:

错误通知

经典事件需要单独的事件,或者需要检查具有EventArgs属性的Error类。

通知结束通知

经典事件需要单独的事件或具有EventArgs属性的Final类,需要进行检查。

答案 2 :(得分:3)

它只是基于事件的编程模型的扩展。你创建了一些实现IObserver的东西,基本上你说“这就是我想在集合中的某些东西发生变化时发生的事情”。通过这种方式,它只是我们对事件所做的事情的标准化。

与IEnumerable模式相比,他们正在推动它,因为它是一个很大的面孔。 IEnumerable是“pull”,而IObservable是“push”。

我看到直接事件的唯一优势是它是一个标准化的界面。我看到这里与ObservableCollection有很大的重叠(和INotifyCollectionChanged)。也许他们正试图采用PERL格言与.NET:“有不止一种方法可以做到这一点。”

答案 3 :(得分:2)

您一定要观看Rx Workshop: Observables versus Events视频和 完成所附的挑战