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