C# 在Do块中创建可观察到的副作用

C# 在Do块中创建可观察到的副作用,c#,system.reactive,reactive-programming,C#,System.reactive,Reactive Programming,对于反应式扩展,通常建议将Select保留为纯功能,即没有任何副作用。然后,当需要副作用时,要么把它放在管道的末端,然后用订阅,要么作为流水线中的最后一个工具。 理想情况下,我会选择最后一个选项,但在我目前的情况下,类似于下面的代码块,我有一个带有副作用的选择,因为我需要知道操作的结果,以便稍后执行一些逻辑。结果的例子可以是: 操作是否成功完成或失败,以便向用户显示错误 该操作需要一些数据,这是进一步观察管道所需要的 无论如何,这里有一个代码示例: IObservable<Unit>

对于反应式扩展,通常建议将Select保留为纯功能,即没有任何副作用。然后,当需要副作用时,要么把它放在管道的末端,然后用订阅,要么作为流水线中的最后一个工具。 理想情况下,我会选择最后一个选项,但在我目前的情况下,类似于下面的代码块,我有一个带有副作用的选择,因为我需要知道操作的结果,以便稍后执行一些逻辑。结果的例子可以是:

操作是否成功完成或失败,以便向用户显示错误

该操作需要一些数据,这是进一步观察管道所需要的

无论如何,这里有一个代码示例:

IObservable<Unit> inputStream = /* generate stream... */;

IObservable<OperationResult> operationOneStream = inputStream.Select(_ => 
{
  try 
  {
    /* side effect: do something, that can also throw... */

    return OperationResult.Success;  // use a simple Enum
  }
  catch (Exception ex)
  {
    /* compensate error: do something ... */

    return OperationResult.ErrorInOperationOne;
  }
});

// operationOneStream is used to build further streams down the chain, it's not "final".

IObservable<OperationResult> operationTwoStream = operationOneStream
  .Where(result => result == OperationResult.Success)
  // here in between some more operations: delay, select returning a stream, switch, ...
  .Select(_ => 
  {
    try 
    {
      /* side effect: do something, that can also throw... */

      return OperationResult.Success;
    }
    catch (Exception ex)
    {
      /* compensate error: do something ... */

      return OperationResult.ErrorInOperationTwo;
    }
  });

// then a third operation, running when nr. 2 is successful

/* ... */

/* Then, operationOne could be merged with other similar operations, 
 * so to grab their error streams and treat them in a single block.
*/
IObservable<SomeType> allOperationsStream = Observable.Merge(
  operationOneStream, operationTwoStream /*, ... */);

IDisposable subscription = allOperationsStream
  .Where(result => result != OperationResult.Success)
  .Subscribe(result => /* do something with error */);
现在我想了解是否有一种方法可以从Do转换为Subscribe,以便更明确地了解这些副作用。在这种情况下,Subscribe会停止链,因此,为了获取操作的输出并在以后使用,我会求助于一个主题,并在操作完成或失败时提供该主题。但是,除非真的需要,否则通常不鼓励受试者

是否有更具表现力的代码?谢谢大家


编辑:在各种注释之后,我添加了更多代码,以显示更完整的示例。此外,我还修复了暴露当前情况时出现的一些错误,我认为这些错误令人困惑。

我不确定我是否完全遵循了您的要求,但您可能只是试图使用selectmany将多个观测值链接在一起

(from a in Observable.Return(1)
 from b in Observable.Return(2)
 select b
).Subscribe(x => {}, ex => {})
您还可以将错误处理构建为这样:

(from a in Observable.Return(1)
 from result in Observable.Return(2)
                          .Select(x => OperationResult.Success)
                          .Catch<OperationResult, Exception>(ex => { return Observable.Return(OperationResult.Error);})
  select result)
  .Subscribe(x => {}, ex => {});

Do不会返回任何内容,因此它不会在以后的管道中产生任何实际影响—可能是Select?中的换位错误?。你能举个例子说明你打算在以后的研究中如何使用副作用吗?您当前的示例让您执行一个操作,并将结果传递到流的末尾,这与您应该如何使用Rx完全一致,没有任何明显的副作用。我希望不要深入操作的细节,让我们看看。这个场景中的输入流是由我目前无法控制的常规网络事件生成的。其中一些所谓的操作会产生副作用,因为如果它们成功,会引发更多的事件,从而产生新的可观察值。另一方面,如果操作失败,则不会引发进一步的事件,以后也不会出现新的可观察事件,这很好:但我抓取错误并将其显示给用户&log。这更清楚一点吗?感谢您对构建错误处理的建议:我将检查是否可以将其应用于当前场景。试图回答你的问题:如果我想:*在可观察的事件中执行一些操作,*保留其副作用,*将操作成功反馈给进一步可观察的对象,*将操作失败反馈给进一步可观察的对象,如果我想:*在可观察的事件中执行某些操作,*保留其副作用,*将操作成功反馈给进一步可观察的对象,*将操作失败反馈给进一步可观察的对象您的错误处理建议,1个问题:如果捕获异常并将其转换为常规OperationResult值,则不再存在异常,那么为什么在Subscribe中使用OneError处理程序?我想说,处理流中可能发生的任何其他错误总是一个好主意。因此,在这种情况下,当OperationResult未成功时,您将在OnNext中处理错误,在OnError中也是如此?这实际上取决于当您检测到一个可观察的流抛出异常时您想要做什么。如果第二个可观察对象可能出现错误,但您仍然希望继续处理第一个可观察对象中的项目,那么如上所述捕获它,记录错误,并返回一个空的可观察对象,以便内部可观察对象完成。另一方面,如果您无法从抛出异常的任何可观察对象中恢复,那么您也可以让异常流通过Subscribe块中的错误处理程序。一旦发生这种情况,就不会再发生OnNexts。