我只是想知道何时使用每个以及每个的优点我真的很难理解为什么.net在事件委托之后引入IObservable / IObserver并且根据MSDN事件委托也是首选
基于我们对Observer模式的理解,现在让我们将注意力转向在.NET Framework中使用此模式。那些熟悉FCL中公开的类型的人会注意到框架中不存在IObserver,IObservable或ObservableImpl类型*。他们缺席的主要原因是CLR使它们在时尚之后过时。虽然您当然可以在.NET应用程序中使用这些构造,但*委托和事件的引入提供了一种新的强大的方法来实现Observer模式,而无需开发专用于支持此模式的特定类型。实际上,由于委托和事件是CLR的第一类成员,因此该模式的基础已合并到.NET Framework的核心中。因此,FCL在其整个结构中广泛使用Observer模式。
那么为什么他们将IObservable添加到.net 4.0
我在MSDN网站上找到了引用文本的引用。我不得不说我很惊讶。这似乎是对IObservable<T>
实施的不明智和错误观点。
那些熟悉FCL中暴露的类型的人会注意到框架中没有
IObserver
,IObservable
或ObservableImpl
类型。
这是对的。我们得到的IObservable<T>
和IObserver<T>
接口是IEnumerable<T>
和IEnumerator<T>
的数学对偶。它将集合从您同步请求的值转换为异步向您推送值的内容。以下是Matthew Podwysocki关于二元性的说法:
我们从系列中的第一篇文章中记得,我们谈到了拉(交互)和推(反应)模型。由
IEnumerable<T>
/IEnumerator<T>
的迭代器模式表示的拉模型表明我们必须显式调用一个方法,以便从我们的抽象集合中获取每个项目。另一方面,我们的推模型,由IObservable<T>
/IObserver<T>
的可观察模式表示,我们通过订阅注册兴趣,然后项目随后从一些抽象的集合交给我们。
回到你引用的文字。
他们缺席的主要原因是CLR使它们在时尚之后过时。虽然您当然可以在.NET应用程序中使用这些构造,但委托和事件的引入提供了一种新的强大的方法来实现Observer模式,而无需开发专用于支持此模式的特定类型。
这似乎完全倒退了。自v1.0以来,代表和事件一直在框架中。如果他们使IObservable<T>
/ IObserver<T>
过时,那么就没有必要介绍它们(这是你问题的关键)。
我对时间的记忆是,微软非常相信这对接口的价值,他们急于将它们包含在BCL中,以便开发人员可以在完整的System.Reactive
实现发布之前编写自己的基本代码。
实际上,由于委托和事件是CLR的第一类成员,因此该模式的基础已合并到.NET Framework的核心中。因此,FCL在其整个结构中广泛使用Observer模式。
这又是对“一等成员”意味着什么的兴趣观点。 Wikipedia说:
在编程语言设计中,给定编程语言中的第一类公民(也是类型,对象,实体或价值)是支持通常可用于其他实体的所有操作的实体。这些操作通常包括作为参数传递,从函数返回,修改并分配给变量。
事件肯定不是一等公民。你不能传递一个事件,你不能独立地向宣布它的类提出一个事件。
举个简单的例子:
void Main()
{
var foo = new Foo();
EventHandler bar = foo.Bar;
}
public class Foo
{
public event EventHandler Bar;
public void OnBar()
{
this.Bar?.Invoke(this, new EventArgs());
}
}
我尝试编译时收到以下错误:
CS0070事件'Foo.Bar'只能出现在+ =或 - =的左侧(除非在'Foo'类型中使用)
如果我有一个定义事件的类的实例的引用,我只能订阅一个事件,我只能从同一个类中引发事件。
Observables不是这样。
这段代码编译得很好:
void Main()
{
var foo = new Foo();
IObservable<EventPattern<EventArgs>> bar =
Observable
.FromEventPattern<EventHandler, EventArgs>(
h => foo.Bar += h,
h => foo.Bar -= h);
}
public void SimpleExample(IObservable<EventPattern<EventArgs>> example)
{
example.Subscribe(x => { });
}
public class Foo
{
public event EventHandler Bar;
public void OnBar()
{
this.Bar?.Invoke(this, new EventArgs());
}
}
Observable是C#(和VB.NET和F#)的一等公民,但事件不是。
虽然标准事件模型是观察者模式的一种形式,但它并不总是易于使用。
试试这段代码:
void Main()
{
var foo = new Foo();
foo.Bar += (s, e) => Console.WriteLine("Bar!");
foo.Bar -= (s, e) => Console.WriteLine("Bar!");
foo.OnBar();
}
public class Foo
{
public event EventHandler Bar;
public void OnBar()
{
this.Bar?.Invoke(this, new EventArgs());
}
}
在运行时,它仍会生成“Bar!”在控制台上。
要正确取消订阅,您必须保留对原始处理程序的引用。这有效:
void Main()
{
var foo = new Foo();
EventHandler handler = (s, e) => Console.WriteLine("Bar!");
foo.Bar += handler;
foo.Bar -= handler;
foo.OnBar();
}
public class Foo
{
public event EventHandler Bar;
public void OnBar()
{
this.Bar?.Invoke(this, new EventArgs());
}
}
Observables处理得更清晰:
void Main()
{
var foo = new Foo();
IObservable<EventPattern<EventArgs>> bar =
Observable
.FromEventPattern<EventHandler, EventArgs>(
h => foo.Bar += h,
h => foo.Bar -= h);
IDisposable subscription = SimpleAttach(bar);
SimpleDetach(subscription);
foo.OnBar();
}
public IDisposable SimpleAttach(IObservable<EventPattern<EventArgs>> example)
{
return example.Subscribe(x => Console.WriteLine("Bar!"));
}
public void SimpleDetach(IDisposable subscription)
{
subscription.Dispose();
}
public class Foo
{
public event EventHandler Bar;
public void OnBar()
{
this.Bar?.Invoke(this, new EventArgs());
}
}
不仅可以传递事件(可观察的),而且可以传递分离处理程序的能力而无需引用原始处理程序本身。在我的例子中,Console.WriteLine("Bar!")
甚至没有取消订阅的相同方法。
这使得能够执行诸如使用单个List<IDisposable> disposables
将所有订阅存储在单个位置以便可以用于从所有事件中干净地分离的事情。只是做disposables.ForEach(x => x.Dispose);
。
并且Observables在单个查询中组合多个范例非常棒。像这样:
void Main()
{
var foo = new Foo();
IObservable<EventPattern<EventArgs>> bar =
Observable
.FromEventPattern<EventHandler, EventArgs>(
h => foo.Bar += h,
h => foo.Bar -= h);
var query =
from ep in bar
from name in Observable.FromAsync(() => GetNameAsync()) // async Task<string> GetNameAsync()
from data in Observable.Start(() => LoadData(name)) // string LoadData(string name)
select new { name, data };
var subscription =
query
.Subscribe(x => Console.WriteLine($"{x.name} = {x.data}"));
}
query
美丽而简洁。
我很少使用标准事件模型。我几乎总是使用Observables。
添加了Observable,因为它们仍然是观察者模式比内置事件模型更强大的抽象。