当我尝试从本机.NET事件创建的IObservable获取IEnumerable时,IEnumerable会在查询第一个元素时阻塞。我做错了什么?
我已经建立了一个小完整的例子。第一个测试方法阻塞,尽管事件被另一个IObservable正确推送。第二种测试方法适用于普通数组,不会阻塞。
谢谢!
using System;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace TestGTI.Reactive
{
class ItHappenedEventArgs : EventArgs
{
}
class A
{
public event EventHandler<ItHappenedEventArgs> ItHappened;
public void RaiseItHappened(ItHappenedEventArgs e)
{
if (ItHappened != null)
{
ItHappened(this, e);
}
}
}
[TestClass]
public class ReactiveTest
{
[TestMethod]
public void EnumerateEventTest()
{
var a = new A();
ItHappenedEventArgs pushed = null;
Observable.FromEvent<ItHappenedEventArgs>(a, "ItHappened").Subscribe(e =>
{
pushed = e.EventArgs;
});
var itHappenedEnum = Observable.FromEvent<ItHappenedEventArgs>(a, "ItHappened").ToEnumerable();
var itHappenedEventArgs = new ItHappenedEventArgs();
a.RaiseItHappened(itHappenedEventArgs);
Assert.AreSame(itHappenedEventArgs, pushed);
// blocks!!!
Assert.AreSame(itHappenedEventArgs, itHappenedEnum.First());
}
[TestMethod]
public void ObservableToEnumerableTest()
{
var array = new int[] { 1, 2, 3 };
var enumerable = array.ToObservable().ToEnumerable();
// works
Assert.AreEqual(1, enumerable.First());
}
}
}
答案 0 :(得分:3)
枚举时IEnumerable
是懒惰的,ToEnumerable
在您开始枚举之前不会订阅源。
在您的示例中,您是在之后第一次引发事件时调用First()
,因此您订阅源并且没有发出任何值,因此阻塞。基本上在你聆听的时候,价值已经过去了。
如果您想记住该值,可以使用Prune
或Replay
(请记住连接到返回的IConnectableObservable
以开始收听)。然后,您可以使用ToEnumerable
并发出值。
答案 1 :(得分:0)
理查德就在这里,但我也从概念视图中添加它 - IEnumerable通常(在实践中,但不是必须的)是有限大小列表的抽象。 FromEvent是一个永不终止的IObservable - 创建一个无限大小的列表是否有意义?我无法用很多实际的东西......