我只想知道何时使用它们以及它们的优点 我真的很难理解为什么.net在拥有事件委托后才引入IObservable / IObserver 并据此首选MSDN事件委托
基于对Observer模式的理解,现在让我们将注意力转向在.NET Framework中使用此模式。那些熟悉FCL中公开的类型的人会注意到,框架中不存在IObserver,IObservable或ObservableImpl类型*。缺少它们的主要原因是CLR使它们过时而过时。尽管您当然可以在.NET应用程序中使用这些构造,但是* delegates和event的引入为实现Observer模式提供了一种新的强大功能,而无需开发专用于支持该模式的特定类型。实际上,由于委托和事件是CLR的一等成员,因此该模式的基础已并入.NET Framework的核心。因此,FCL在其整个结构中都广泛使用了Observer模式。
为什么他们将IObservable添加到.net 4.0
答案 0 :(得分:4)
我在MSDN网站上找到了对引用文字的引用。我不得不说我很震惊。对于IObservable<T>
的实现,这似乎是一个错误的,错误的看法。
那些熟悉FCL中公开的类型的人会注意到,框架中没有
IObserver
,IObservable
或ObservableImpl
类型。
这是正确的。 IObservable<T>
和IObserver<T>
接口是IEnumerable<T>
和IEnumerator<T>
的数学对偶。它将集合从您同步请求值转变为异步将值推送给您的集合。 Matthew Podwysocki说了对偶性:
我们记得在本系列的第一篇文章中,我们谈到了拉(交互式)与推(响应)模型。由
IEnumerable<T>
/IEnumerator<T>
的迭代器模式表示的pull模型指出,为了从抽象集合中获取每个项目,我们必须显式调用一个方法。另一方面,以IObservable<T>
/IObserver<T>
的可观察模式表示的推送模型指出,我们通过订阅来注册兴趣,然后随后从一些抽象的集合中将项目交给我们。>
返回引用的文本。
缺少它们的主要原因是CLR使它们过时了而过时了。尽管您当然可以在.NET应用程序中使用这些构造,但是委托和事件的引入为实现Observer模式提供了一种新的强大功能,而无需开发专用于支持该模式的特定类型。
这对我来说似乎完全是倒退。自v1.0以来,代表和事件一直在框架中。如果它们使IObservable<T>
/ IObserver<T>
过时,那么就不需要引入它们了(这是您所要解决的问题)。
我当时的记忆是,Microsoft对这对接口的价值深信不疑,因此他们急于将它们包含在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());
}
}
可观察对象是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。
添加了可观察对象,因为它们比过去的事件模型仍然强大得多,是对观察者模式的抽象。