[英]Advantages of .NET Rx over classic events?
您可以将IObservable用作事件,替换使用IObservable类型的属性公开事件的代码,但这不是重点。
关于IObservable有两个重要的事情要理解:
它统一了我们之前不知道如何统一的两个概念 :异步操作(通常返回单个值)和事件(通常永远持续)。
它是可组合的 。 与CLR事件,IAsyncResult或INotifyCollectionChanged不同,它允许我们从一般事件和异步操作中构建特定事件。
这是我今天下午在工作中遇到的一个例子。
在Silverlight中,有一些效果可以应用于无法应用于普通控件的图像控件。 为了解决控件内容更改时的这些限制,我可以等待其视觉外观更新并截取它的屏幕截图。 然后我想隐藏其可视化表示,用快照替换它,并将视觉效果应用于图像。 现在我可以将图像效果应用于控件(假设它不是交互式的)。
这个程序将是微不足道的,但它必须是异步的。 在将效果应用于图像之前,我必须等待两个连续的异步操作才能完成:
这是我如何使用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”)。 当您以这种方式构建程序时(完全创建我需要的事件,然后通过更改状态来响应它),您会发现它们具有更少的状态错误,变得更有序,并且完全更自我描述。
得到它了? :-)
我不确定这些优点,但我发现以下与经典.NET事件的区别:
错误通知
经典事件需要单独的事件,或者需要检查具有Error
属性的EventArgs
类。
通知结束通知
经典事件需要为此事件或具有需要检查的Final
属性的EventArgs
类单独的事件。
它只是基于事件的编程模型的扩展。 你创建了一些实现IObserver的东西,基本上你说“这就是我想在集合中的某些东西发生变化时发生的事情”。 通过这种方式,它只是我们对事件所做的事情的标准化。
与IEnumerable模式相比,他们正在推动它,因为它是一个很大的面孔。 IEnumerable是“拉”,而IObservable是“推”。
我在直接事件中看到的唯一优势是它是一个标准化的界面。 我看到这里与ObservableCollection有很大的重叠(和INotifyCollectionChanged)。 也许他们正试图采用PERL格言与.NET:“有不止一种方法可以做到”。
您一定要观看Rx Workshop:Observables vs Events视频并完成附加的挑战赛
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.