C# 使用数据流管道继续处理循环

C# 使用数据流管道继续处理循环,c#,tpl-dataflow,C#,Tpl Dataflow,我正在玩弄数据流,并试图学习如何使用它们。我已经找到了很多例子来说明如何使用不同的块,但是没有一个真正解释了如何处理异常 我的主要问题是,如果发生异常或前一个转换块的输出不是您所期望的,如何继续foreach循环。下面是我用来测试的一个简单的Windows窗体应用程序。它只是一个按钮,可以循环显示一系列数字 我在操作块中添加了一个if语句,该语句表示如果数字=5,则抛出一个异常。循环似乎在遇到异常后继续处理,但在遇到异常后停止写入输出。该异常也不会出现在foreach循环中的catch子句中 u

我正在玩弄数据流,并试图学习如何使用它们。我已经找到了很多例子来说明如何使用不同的块,但是没有一个真正解释了如何处理异常

我的主要问题是,如果发生异常或前一个转换块的输出不是您所期望的,如何继续foreach循环。下面是我用来测试的一个简单的Windows窗体应用程序。它只是一个按钮,可以循环显示一系列数字

我在操作块中添加了一个if语句,该语句表示如果数字=5,则抛出一个异常。循环似乎在遇到异常后继续处理,但在遇到异常后停止写入输出。该异常也不会出现在foreach循环中的catch子句中

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks.Dataflow;
using System.Windows.Forms;

namespace DataFlowsTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            List<int> TestList = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

            var actionBlock = new ActionBlock<int>(item =>
            {
                if (item == 5)
                    throw new Exception("Blech.");
                Debug.WriteLine(item.ToString());
            });


            foreach(var number in TestList)
            {
                try
                {
                    actionBlock.Post(number);
                }
                catch(AggregateException ex)
                {
                    Debug.WriteLine(ex.Message);
                    continue;
                }
            }
            actionBlock.Complete();
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用系统诊断;
使用System.Threading.Tasks.Dataflow;
使用System.Windows.Forms;
命名空间数据流测试
{
公共部分类Form1:Form
{
公共表格1()
{
初始化组件();
}
私有无效按钮1\u单击(对象发送者,事件参数e)
{
List TestList=newlist(){1,2,3,4,5,6,7,8,9,10};
var actionBlock=新的actionBlock(项=>
{
如果(项目==5)
抛出新异常(“Blech”);
Debug.WriteLine(item.ToString());
});
foreach(测试列表中的变量编号)
{
尝试
{
actionBlock.Post(编号);
}
捕获(聚合异常)
{
Debug.WriteLine(例如消息);
继续;
}
}
actionBlock.Complete();
}
}
}
此代码返回 1. 2. 3. 4. 引发异常:DataFlowsTest.exe中的“System.Exception” DataFlowsTest.exe中发生“System.exception”类型的异常,但未在用户代码中处理
Blech.

您正在抛出一个
异常
,但只捕获
聚合异常


为泛型(
Exception ex
)或要捕获的类型添加捕获

您正在抛出一个
异常
,但仅捕获
聚合异常


为泛型(
Exception ex
)或要捕获的类型添加捕获,下面是我如何实现它的。如果您对这种方法感兴趣,我可以在Github上分享更多信息。我经常使用数据流,所以我已经基于这种方法实现了很多其他IDataFlow类

积木 本质上,通过将每条消息包装在一个名为
流的类中,我们可以实现一种方法。流有两种状态:失败或成功。成功的流是
,在发生故障时被传递到下一个自定义数据流或连接到
故障块:ITargetBlock
。(本质上是一个处理异常、日志等的
ActionBlock

我的基本流类如下所示:

public class Flow<T> : IFlow
{
  public T Value { get; private set; }
  public Exception Exception { get; private set; }
  public bool Success => Exception is null;
  public bool Failure => !Success;
  public void Fail(Exception exception) => Exception = exception;
  public Flow(T value) => Data = value;
  public Flow(Exception exception) => Fail(exception);
  public static Flow<T> FromValue<T>(T data) => new Flow<T>(data);
}
生成的自定义IDataflow 下面的部分看起来很可怕,但不要害怕。它本质上是一个TransformBlock包装器,具有两个额外的功能:

在此处输入代码1。每个自定义
FlowBlock
将方法包装成
try{}catch{}

  • LinkTo
    方法将成功的流链接到下一个块,将失败链接到
    FailureBlock
  • 公共类流块:IPropagatorBlock
    {
    受保护的覆盖ITargetBlock目标=>TransformBlock;
    受保护的覆盖ISourceBlock源=>TransformBlock;
    私有转换块转换块{get;}
    专用故障块故障块{get;}
    公共流程块(
    Func变换,
    ExecutionDataflowBlockOptions dataflowBlockOptions,
    失效块(失效块)
    {
    TransformBlock=新TransformBlock(
    异步流入=>
    {
    尝试
    {
    返回新的流(等待转换(inFlow.Data));
    }
    捕获(异常)
    {
    返回新流程(异常);
    }
    },
    dataflowBlockOptions);
    }
    公共覆盖IDisposable链接到(
    ITargetBlock目标,
    数据流链接选项(链接选项)
    =>新的一次性(
    LinkTo(target,linkOptions,flow=>flow.Success),
    LinkTo(OutputBlock,linkOptions,flow=>flow.Failure));
    }
    

    如果您感兴趣,请在评论中告诉我,我很乐意打开一个github repo,提供更多详细信息。

    以下是我如何实现它的。如果您对这种方法感兴趣,我可以在github上分享更多信息。我经常使用数据流,因此我已经基于这种方法实现了许多其他IDataFlow类

    积木 本质上,通过将每个消息包装在一个名为
    的类中,我们可以实现一种方法。流有两种状态:失败或成功。成功的流是
    ,在失败时传递到下一个自定义数据流或连接到
    FailureBlock:ITargetBlock
    。(本质上是一个处理异常、日志等的
    ActionBlock

    我的基本流类如下所示:

    public class Flow<T> : IFlow
    {
      public T Value { get; private set; }
      public Exception Exception { get; private set; }
      public bool Success => Exception is null;
      public bool Failure => !Success;
      public void Fail(Exception exception) => Exception = exception;
      public Flow(T value) => Data = value;
      public Flow(Exception exception) => Fail(exception);
      public static Flow<T> FromValue<T>(T data) => new Flow<T>(data);
    }
    
    生成的自定义IDataflow 下面的部分看起来很可怕,但不要害怕。它本质上是一个TransformBlock包装器,具有两个额外的功能:

    在此处输入代码1。每个自定义
    FlowBlock
    将方法包装成
    try{}catch{}

  • LinkTo
    方法将成功的流链接到下一个块,将失败链接到
    FailureBlock
  • 公共类流块:IPropagatorBlock
    {
    受保护的覆盖ITargetBlock目标=>TransformBlock;
    受保护覆盖ISourceB
    
    public class FlowBlock<TInput, TOutput>: IPropagatorBlock<Flow<TInput>, Flow<TOutput>>
    {
        protected override ITargetBlock<Flow<TInput>> Target => TransformBlock;
        protected override ISourceBlock<Flow<TOutput>> Source => TransformBlock;
        private TransformBlock<Flow<TInput>, Flow<TOutput>> TransformBlock { get; }
        private FailureBlock FailureBlock { get; }
        public FlowBlock(
            Func<TInput, Task<TOutput>> transform,
            ExecutionDataflowBlockOptions dataflowBlockOptions,
            FailureBlock failureBlock)
        {
            TransformBlock = new TransformBlock<Flow<TInput>, Flow<TOutput>>(
                async inFlow =>
                {
                    try
                    {
                        return new Flow<TOutput>(await transform(inFlow.Data));
                    }
                    catch (Exception exception)
                    {
                        return new Flow<TOutput>(exception);
                    }
                },
                dataflowBlockOptions);
        }
        public override IDisposable LinkTo(
            ITargetBlock<Flow<TOutput>> target,
            DataflowLinkOptions linkOptions)
            => new Disposable(
                Source.LinkTo(target, linkOptions, flow => flow.Success),
                Source.LinkTo(OutputBlock, linkOptions, flow => flow.Failure));
    }
    
    var actionBlock = new ActionBlock<int>(item =>
    {
        try
        {
            if (item == 5)
                throw new Exception("Blech.");
            Debug.WriteLine(item.ToString());
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex);
        }
    
    });
    
    foreach (var number in TestList)
    {
        if(!actionBlock.Post(number))
            actionBlock.Complete();
        try
        {
            await actionBlock.Completion;
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
            //actionBlock is now dead
            break;
        }
    }