代码之家  ›  专栏  ›  技术社区  ›  Wim Coenen

.NET RX相对于经典事件的优势?

  •  28
  • Wim Coenen  · 技术社区  · 16 年前

    .NET 4.0 beta 2 introduced 这个 IObservable IObserver 接口。

    与经典的.NET事件相比有什么优势?这不是解决了同样的问题吗?

    4 回复  |  直到 10 年前
        1
  •  35
  •   user149327    16 年前

    可以将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编程时,诀窍是思考“我希望我的框架提供什么事件?”然后去创造它。我们被训练将事件看作简单的、由输入驱动的事物(“鼠标悬停”、“鼠标悬停”、“键控”等)。但是,没有理由事件不能非常复杂,并且特定于您的应用程序(“GoogleMsdnmashupstockDataReached”、“DragStarting”和“ImageContentChanged”)。当你以这种方式组织你的程序时(准确地创建我需要的事件 然后 通过改变状态来响应它)您会发现它们的状态错误更少,更有序,并且更自我描述。

    知道了?-)

        2
  •  4
  •   Wim Coenen    10 年前

    我不确定其优势,但我发现与经典.NET事件有以下不同:

    错误通知

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

    通知结束通知

    经典事件将需要一个单独的事件 意外事件 用A类 Final 需要检查的属性。

        3
  •  3
  •   Scott Whitlock    16 年前

    它只是对基于事件的编程模型的扩展。您创建了实现iobserver的东西,基本上您是在说“当集合中的某个东西发生变化时,我希望发生的事情就是这样”。那样的话,这只是我们所有人对事件所做的标准化。

    他们把它推得像一张脸,而不是一个数不清的图案。IEnumerable是“pull”,而IObservable是“push”。

    与直接事件相比,我看到的唯一优势是它是一个标准化的接口。不过,我发现这里的ObservableCollection(和inotifyCollectionChanged)有很大的重叠。也许他们正在尝试将Perl的座右铭与.NET结合起来:“有不止一种方法可以做到这一点”。

        4
  •  2
  •   Alexander Bartosh    13 年前

    你一定要看 Rx Workshop: Observables versus Events 视频和 完成附加的挑战