C# 链接观察值和传递结果值

C# 链接观察值和传递结果值,c#,system.reactive,C#,System.reactive,我想将以下代码转换为反应流(使用C#中的System.reactive) Method1、Method2和Method3都是长时间运行的任务。调用Method3需要Method2的结果,但是Method1和Method2可以并行运行。如果result1==null可以使用null提前终止整个操作 通常,Method2的返回速度比Method1快,因此可以在Method1完成之前启动Method3 var result1=Method1(); if(result1==null)返回null; va

我想将以下代码转换为反应流(使用C#中的
System.reactive

Method1
Method2
Method3
都是长时间运行的任务。调用
Method3
需要
Method2
的结果,但是
Method1
Method2
可以并行运行。如果
result1==null
可以使用
null
提前终止整个操作

通常,
Method2
的返回速度比
Method1
快,因此可以在
Method1
完成之前启动
Method3

var result1=Method1();
if(result1==null)返回null;
var result2=Method2();
字符串result3=null;
如果(result2!=null)
{
结果3=方法3(结果2);
}
var combinedResult=CreateResult(result1);
如果(result2!=null)
{
combinedResult.Attr2=结果2;
}
if(result3!=null)
{
combinedResult.Attr3=结果3;
}
我迷失在嵌套函数和泛型之间。以下代码无法编译,因为它在泛型和返回类型方面存在问题(尤其是嵌套的
Select
不返回值,而是返回可观察的

var observe1=observeable.Start(()=>Method1());
var observe2=observeable.Start(()=>Method2());
可观察的.Zip(可观察的1,可观察的2,(结果1,结果2)=>
{
如果(result2!=null)
{
var Observable 3=可观察的。开始(()=>Method3(result2));
返回observe3.Select(result3=>
{
返回超级组合器(result1、result2、result3);
};
}
返回超级组合器(result1,null,null);
};

我对您的草稿做了一些改进,现在它可以按照您的描述工作了:

var stream1 = Observable.Start(Func1);
var stream2 = Observable.Start(Func2);

Observable.Zip(stream1, stream2, (res1, res2) =>
{
    if (res1 == null)
        return Observable.Start(() => new string[] { null });

    if (res2 == null)
        return Observable.Start(() => new string[] { res1, null });

    return Observable.Start(() => Func3(res2)).Select(res3 => new[] { res1, res2, res3 });
})
.Merge()
.Subscribe(result =>
{
    // 'result' is an array

    // result[0] - result of Func1
    // result[1] - result of Func2
    // result[2] - result of Func3

    // result.Length == 1  - means that Func1 returned 'null'
    // result.Length == 2  - means that Func2 returned 'null'
});

但它不是一种真正的“反应式”方式,因为它包含命令语句(例如
if
运算符)。

以下是使其工作所需的:

var inner =
    from m2 in Observable.Start(() => Method2())
    from m3 in Observable.Start(() => Method3(m2))
    select new { m2, m3 };

var query =
    Observable
        .Start(() => Method1())
        .Publish(m1s =>
            m1s
                .Zip(
                    inner.TakeUntil(m1s.Where(m1 => m1 == null)),
                    (m1, m23) => new { m1, m23.m2, m23.m3 }))
        .Where(x => x.m1 != null);
我已使用以下代码对此进行了测试:

public string Method1()
{
    Console.WriteLine("Method1 Start");
    Thread.Sleep(TimeSpan.FromSeconds(2.0));
    Console.WriteLine("Method1 End");
    return null; //"1";
}


public string Method2()
{
    Console.WriteLine("Method2 Start");
    Thread.Sleep(TimeSpan.FromSeconds(3.0));
    Console.WriteLine("Method2 End");
    return "2";
}

public string Method3(string x)
{
    Console.WriteLine("Method3 Start");
    Thread.Sleep(TimeSpan.FromSeconds(2.0));
    Console.WriteLine("Method3 End");
    return $"3-{x}";
}
只有当
Method1
返回非空值时,查询才会生成一个值,否则它将在不生成值的情况下完成

Method3
Method2
完成后立即执行,除非
Method1
已返回
null
,在这种情况下,不会执行
Method3


这是您所要求的计算效率最高的实现。

谢谢,您的解决方案正在运行。我只需将
订阅
替换为
第一异步
,这样我就可以
等待
并回到同步世界。这里效率低下。
方法3
等待,直到两个
方法都出现1
Method2
已完成。OP要求“
Method3
可以在
Method1
完成之前启动”,但这在该代码中不会发生。我同意:上面的代码是“
Method1和Method2
并行的,只有这样才会调用
Method3
”.与此同时,我还实施了一个定期“任务”解决方案,这正是我想要的。但我仍然希望找到“理想”反应式解决方案-由于给定的约束条件,我知道这不是真正的“反应式”。我同意您的代码比我的代码效率高一点。但是您关于方法3的陈述是不正确的。方法3在任何情况下都将被执行。更重要的是,即使
查询
通知它已完成,它仍将执行到最后因为没有人阻止它。如果我们谈论的是效率,那么在这种特殊情况下,“任务+取消令牌”而不是“可观察的”将是最有效的方式。@VitaliiIlchenko-你是说
Method3
将在我的代码中执行吗?@Vitaliiiilchenko-a
CancellationToken
只会更有效nt如果方法支持合作取消。是的,它会。只需为Thread.Sleep设置另一个延迟,您就会看到。例如:Method1 Sleep-3秒、Method2 Sleep-2秒和Method3 Sleep-4秒。如果有这样的延迟,Method3将始终被调用。由于它被调用,它将被执行到最后。