Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/272.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 同时观察同步和异步结果_C#_Asynchronous_System.reactive - Fatal编程技术网

C# 同时观察同步和异步结果

C# 同时观察同步和异步结果,c#,asynchronous,system.reactive,C#,Asynchronous,System.reactive,使用Rx,我想观察一个遗留对象,它同时公开方法GetItems和事件NewItem 调用GetItems时,它将同步返回缓存中所有项的列表。它还将生成一个异步获取项,这些项在接收时将通过NewItem事件发布 如何通过制定LINQ查询以捕获两个结果集,以一致的方式观察这两个源(同步+异步)?生产订单不重要。让我看看我是否了解您的遗留对象。我假设它是一种泛型类型,如下所示: public class LegacyObject<T> { public IEnumerable<

使用Rx,我想观察一个遗留对象,它同时公开方法
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