C# 同时观察同步和异步结果
使用Rx,我想观察一个遗留对象,它同时公开方法C# 同时观察同步和异步结果,c#,asynchronous,system.reactive,C#,Asynchronous,System.reactive,使用Rx,我想观察一个遗留对象,它同时公开方法GetItems和事件NewItem 调用GetItems时,它将同步返回缓存中所有项的列表。它还将生成一个异步获取项,这些项在接收时将通过NewItem事件发布 如何通过制定LINQ查询以捕获两个结果集,以一致的方式观察这两个源(同步+异步)?生产订单不重要。让我看看我是否了解您的遗留对象。我假设它是一种泛型类型,如下所示: public class LegacyObject<T> { public IEnumerable<
GetItems
和事件NewItem
调用GetItems
时,它将同步返回缓存中所有项的列表。它还将生成一个异步获取项,这些项在接收时将通过NewItem
事件发布
如何通过制定LINQ查询以捕获两个结果集,以一致的方式观察这两个源(同步+异步)?生产订单不重要。让我看看我是否了解您的遗留对象。我假设它是一种泛型类型,如下所示:
public class LegacyObject<T>
{
public IEnumerable<T> GetItems();
public event EventHandler<NewItemEventArgs<T>> NewItem;
}
public class NewItemEventArgs<T> : System.EventArgs
{
public T NewItem { get; private set; }
public NewItemEventArgs(T newItem)
{
this.NewItem = newItem;
}
}
var tester = new Subject<int>();
var legacy = new LegacyObject<int>(new [] { 1, 2, 3, }, tester);
var values = legacy.ToObservable();
values.Subscribe(v => Console.WriteLine(v));
tester.OnNext(3);
tester.OnNext(4);
tester.OnNext(4);
tester.OnNext(5);
legacyObject.GetItems().ToObservable()
.Merge(
Observable.FromEventPattern<IntEventArgs>(legacyObject, "NewItem")
.Select(e => e.EventArgs.Value));
这个方法为每个订阅者创建一个新的可观察对象——这是编写这样的扩展方法时正确的做法
它创建一个门
对象来锁定对内部列表的访问
因为您说过调用GetItems
会产生异步函数来获取新项目,所以我确保NewItem
订阅是在调用GetItems
之前创建的
internal
订阅检查新项目是否在列表中,如果主题不在列表中,则只调用OnNext
调用GetItems
,并通过AddRange
将值添加到内部列表中
在NewItem
事件开始在另一个线程上触发之前,不太可能(但可能)将这些项目添加到列表中。这就是为什么在访问列表时会有一个锁。内部
订阅将等待获得锁后再尝试向列表中添加项目,这将在初始项目添加到列表后发生
最后,内部列表变成一个可观察对象,与主体连接,并且o
观察者订阅这个可观察对象
使用CompositeDisposable
将这两个订阅作为单个IDisposable
返回
这就是ToObservable
方法
现在,我通过在遗留对象上创建一个构造函数来测试这一点,该构造函数允许我传入一个可枚举值和一个可观察值。调用GetItems
时返回可枚举项,可观察项驱动NewItem
事件
因此,我的测试代码如下所示:
public class LegacyObject<T>
{
public IEnumerable<T> GetItems();
public event EventHandler<NewItemEventArgs<T>> NewItem;
}
public class NewItemEventArgs<T> : System.EventArgs
{
public T NewItem { get; private set; }
public NewItemEventArgs(T newItem)
{
this.NewItem = newItem;
}
}
var tester = new Subject<int>();
var legacy = new LegacyObject<int>(new [] { 1, 2, 3, }, tester);
var values = legacy.ToObservable();
values.Subscribe(v => Console.WriteLine(v));
tester.OnNext(3);
tester.OnNext(4);
tester.OnNext(4);
tester.OnNext(5);
legacyObject.GetItems().ToObservable()
.Merge(
Observable.FromEventPattern<IntEventArgs>(legacyObject, "NewItem")
.Select(e => e.EventArgs.Value));
让我知道这是否满足您的需要。如果我正确理解了您的问题,可以使用以下方法:
public class LegacyObject<T>
{
public IEnumerable<T> GetItems();
public event EventHandler<NewItemEventArgs<T>> NewItem;
}
public class NewItemEventArgs<T> : System.EventArgs
{
public T NewItem { get; private set; }
public NewItemEventArgs(T newItem)
{
this.NewItem = newItem;
}
}
var tester = new Subject<int>();
var legacy = new LegacyObject<int>(new [] { 1, 2, 3, }, tester);
var values = legacy.ToObservable();
values.Subscribe(v => Console.WriteLine(v));
tester.OnNext(3);
tester.OnNext(4);
tester.OnNext(4);
tester.OnNext(5);
legacyObject.GetItems().ToObservable()
.Merge(
Observable.FromEventPattern<IntEventArgs>(legacyObject, "NewItem")
.Select(e => e.EventArgs.Value));
legacyObject.GetItems().ToObservable()
.合并(
可观察的。FromEventPattern(legacyObject,“NewItem”)
.Select(e=>e.EventArgs.Value));
编辑:Enigmativity在上面的解决方案中发现了竞争条件(请参见下面的注释)。这一点有望解决:
Observable.FromEventPattern<IntEventArgs>(legacyObject, "NewItem")
.Select(e => e.EventArgs.Value)
.Merge(Observable.Defer(() => legacyObject.GetItems().ToObservable()));
Observable.FromEventPattern(legacyObject,“NewItem”)
.Select(e=>e.EventArgs.Value)
.Merge(Observable.Defer(()=>legacyObject.GetItems().ToObservable());
如果有帮助的话,下面是我的测试代码的其余部分。我不知道我是否准确地建模了您的遗留类:
class IntEventArgs : EventArgs
{
public IntEventArgs(int value)
{
Value = value;
}
public int Value { get; private set; }
}
class Legacy
{
public event EventHandler<IntEventArgs> NewItem;
public IList<int> GetItems()
{
GenerateNewItemAsync();
return new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
}
private void GenerateNewItemAsync()
{
Task.Factory.StartNew(() =>
{
for (var i = 0; i < 100000; ++i)
{
var handler = NewItem;
if (handler != null) handler(this, new IntEventArgs(i));
Thread.Sleep(500);
}
});
}
}
class Example
{
static void Main()
{
var legacyObject = new Legacy();
legacyObject.GetItems().ToObservable()
.Merge(
Observable.FromEventPattern<IntEventArgs>(legacyObject, "NewItem")
.Select(e => e.EventArgs.Value))
.Subscribe(Console.WriteLine);
Console.ReadLine();
}
}
类IntEventArgs:EventArgs
{
公共智能目标(智能值)
{
价值=价值;
}
公共int值{get;private set;}
}
阶级遗产
{
公共事件处理程序NewItem;
公共IList GetItems()
{
GenerateNewItemAsync();
返回新列表{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
}
私有void GenerateNewItemAsync()
{
Task.Factory.StartNew(()=>
{
对于(变量i=0;i<100000;++i)
{
var handler=NewItem;
if(handler!=null)handler(this,newinteventargs(i));
睡眠(500);
}
});
}
}
课例
{
静态void Main()
{
var legacyObject=newlegacy();
legacyObject.GetItems().ToObservable()
.合并(
可观察的。FromEventPattern(legacyObject,“NewItem”)
.Select(e=>e.EventArgs.Value))
.Subscribe(Console.WriteLine);
Console.ReadLine();
}
}
您所说的“一致性”是什么意思?你的意思是“在处理NewItem中的任何项目之前,先处理GetItems中的所有项目?”@Chris:很抱歉含糊不清-我想制定一个LINQ查询,以便捕获两个结果集。生产订单并不重要。@JohannGerell:Enigmativity在我最初的解决方案中发现了竞争条件。请看我的最新答案。谢谢-我太激动了!你说过这个方法为每个订阅者创建了一个新的可观察对象——这是编写这样的扩展方法时正确的做法。-你有我能读到的参考资料吗?@JohannGerell-见第17页。太好了!(实际上,我在提问之前浏览了该文档,但该特定内容没有坚持…;-)此解决方案存在竞争条件,因为GetItems
启动事件I
等于0
时,在订阅事件处理程序之前触发第一个值。事实上,如果您将线程.Sleep(50)
从GenerateNewItemAsync
方法中取出,那么在我的测试中,前34个值(0到33)丢失了。捕捉得好。如果我把溪流的成分倒过来怎么样?让我编辑我的答案,并告诉我你对它的看法。@IlianPinzon-是的,它现在起作用了-特别是因为使用了Defer
。