C# 将基于事件的代码转换为Rx

C# 将基于事件的代码转换为Rx,c#,system.reactive,C#,System.reactive,我有以下代码(为了发布目的而简化) 公共类SomeDataObject { 公共代表无效,已准备就绪; 公共委托无效ErrorEventHandler; 公共活动准备就绪,甚至准备就绪; 公共事件错误事件处理程序错误; ... } 公共类消费类 { 私有SomeDataObject数据对象; 私有任务GetStrings() { 列表结果=新列表(); var tcs=new TaskCompletionSource(); SomeDataObject.ReadyEventHandler Rea

我有以下代码(为了发布目的而简化)

公共类SomeDataObject
{
公共代表无效,已准备就绪;
公共委托无效ErrorEventHandler;
公共活动准备就绪,甚至准备就绪;
公共事件错误事件处理程序错误;
...
}
公共类消费类
{
私有SomeDataObject数据对象;
私有任务GetStrings()
{
列表结果=新列表();
var tcs=new TaskCompletionSource();
SomeDataObject.ReadyEventHandler ReadyHandler=null;
SomeDataObject.ErrorEventHandler ErrorHandler=null;
ReadyHandler+=()=>
{
对于(int i=0;i
其思想是,当进行DoRequest调用时,SomeDataObject将获取一些数据并引发就绪或错误事件(细节不重要!)。如果数据可用,则ItemCount指示有多少项可用

我是Rx新手,找不到任何可比较的示例。那么是否可以将其转换为Rx,从而返回
IObservable
,而不是使用Observable.Create来返回
Task

问候
Alan

我认为下面的代码可以满足您的要求。ReplaySubject用于确保调用者获得所有结果,即使
SomeDataObject
事件立即启动

private IObservable<string> GetStrings()
{
    ReplaySubject<string> results = new ReplaySubject<string>();

    SomeDataObject.ReadyEventHandler ReadyHandler = null;
    SomeDataObject.ErrorEventHandler ErrorHandler = null;

    ReadyHandler += () =>
    {
        for (int i =0; i < dataObject.ItemCount; i++)
            results.OnNext(dataObject[i].ToString());

        results.OnCompleted();
    }

    ErrorHandler += ()
    {
        results.OnError(new Exception("oops!"));
    }

    dataObject.Ready += ReadyHandler;
    dataObject.Error += ErrorHandler;

    dataObject.DoRequest();

    return results;
}
private IObservable GetStrings()
{
ReplaySubject结果=新建ReplaySubject();
SomeDataObject.ReadyEventHandler ReadyHandler=null;
SomeDataObject.ErrorEventHandler ErrorHandler=null;
ReadyHandler+=()=>
{
对于(int i=0;i
Matthew的答案很接近,但有一些问题。首先,它很急切,这通常不符合Rx/函数式编程的精神。其次,我认为您希望能够在消费者处理事件时释放事件句柄。最后,主题的使用应该是一种代码气味,本例中它指出了上述两个问题:-)

在这里,我使用Observable.Create(它应该是工具箱中的#1 goto工具,主题是您的最后手段)来延迟连接,并在处理订阅时提供断开连接/释放事件

private IObservable<string> GetStrings()
{
    return Observable.Create<string>(o=>
    {
        SomeDataObject.ReadyEventHandler ReadyHandler = null;
        SomeDataObject.ErrorEventHandler ErrorHandler = null;

        ReadyHandler += () =>
        {
            for (int i =0; i < dataObject.ItemCount; i++)
                o.OnNext(dataObject[i].ToString());

            o.OnCompleted();
        }

        ErrorHandler += () =>
        {
            o.OnError(new Exception("oops!"));
        }

        dataObject.Ready += ReadyHandler;
        dataObject.Error += ErrorHandler;

        dataObject.DoRequest();

        return Disposable.Create(()=>
            {
                dataObject.Ready -= ReadyHandler;
                dataObject.Error -= ErrorHandler;
            });
    }
}
private IObservable GetStrings()
{
返回可观察的。创建(o=>
{
SomeDataObject.ReadyEventHandler ReadyHandler=null;
SomeDataObject.ErrorEventHandler ErrorHandler=null;
ReadyHandler+=()=>
{
对于(int i=0;i
{
o、 OnError(新异常(“oops!”);
}
dataObject.Ready+=ReadyHandler;
dataObject.Error+=ErrorHandler;
dataObject.DoRequest();
返回一次性。创建(()=>
{
dataObject.Ready-=ReadyHandler;
dataObject.Error-=ErrorHandler;
});
}
}

我也会考虑将<代码> DATAObjs<代码>参数转换成一个参数。在异步系统中共享状态是一个问题的根源。

< P>响应您对李的评论(完全可爱和值得注意)的答案,下面是如何修改他的答案以获得单个<代码>列表>代码>响应和块:

private IObservable<List<string>> GetStrings(SomeDataObject dataObject)
{
    return Observable.Create<List<string>>(o=>
    {
        SomeDataObject.ReadyEventHandler ReadyHandler = null;
        SomeDataObject.ErrorEventHandler ErrorHandler = null;

        ReadyHandler = () =>
        {
            var results = new List<string>(dataObject.ItemCount);
            for (int i =0; i < dataObject.ItemCount; i++)
                results.Add(dataObject[i].ToString());

            o.OnNext(results);
            o.OnCompleted();
        };

        ErrorHandler = () =>
        {
            o.OnError(new Exception("oops!"));
        };

        dataObject.Ready += ReadyHandler;
        dataObject.Error += ErrorHandler;

        dataObject.DoRequest();

        return Disposable.Create(()=>
            {
                dataObject.Ready -= ReadyHandler;
                dataObject.Error -= ErrorHandler;
            });
    });
}
如果使用.NET 4.5,则在
async
方法中,您还可以执行以下操作:

var results = await GetStrings();

您能告诉我们更多关于SomeDataObject API的信息吗?这似乎有点奇怪。调用DoRequest()有效吗每个实例不止一次?如果是,可以在现有请求运行时进行调用吗?它是否设计为具有单个调用,但与多个使用者共享结果?感觉DoRequest应该返回一个句柄,该句柄可用于使用结果(可能是任务或IObservable)。如果出现错误,您希望能够调用DoRequest()吗再次强调?所有这一切都很重要,因为一旦可观察流出现异常,它将不会引发进一步的事件-因此,最好在TResult中表示错误,而不是破坏可观察流或任务等。James,DoRequest只能调用一次。它维护内部状态。它实际上是外部COM库的一部分h我没有控制权。在这种情况下,李的答案是关于钱的。李,这正是我所寻找的。我几乎拥有它,但不能完全理解它的概念。dataObject及其事件实际上是COM库的一部分,我无法控制。我在寻找一种更好的处理事件的方法,而不是任务。你的解决方案非常有效很抱歉,但我现在质疑是否应该将我的代码转换为此。使用Task,我可以让调用方等待完整的结果-我如何使用Rx实现这一点。查看我的答案,查看我调整了Lee的答案,以显示如何获取
列表
,并阻止使用
可观察的结果。wait()
。根据James下面的回答,您可以只输入一个值。我可能会删除for循环并执行类似操作:
o.OnNext(dataObject.Select(o=>o.ToString())
并更新签名。接受Rx James。让那些糟糕的async/Wait和坏掉的.NET任务死掉。;-)我拒绝接受这种明目张胆的诱饵,先生!:)
var results = GetStrings().Wait();
var results = await GetStrings();