我会首先考虑该类的通用界面应该是什么样子。我认为最简单的解决方案是:
public class SplitBlock<TInput, TOutputLeft, TOutputRight>
{
public ITargetBlock<TInput> Input { get; }
public ISourceBlock<TOutputLeft> LeftOutput { get; }
public ISourceBlock<TOutputRight> RightOutput { get; }
}
有了这个,实现自然如下:一个输入块连接到两个输出块。唯一的问题是实际处理是应该在输出块中完成(就像你用两个TransformBlock
s 建议的那样)还是在输入块中完成。
如果您想在输出块中进行处理,输入块可以是一个ActionBlock
将输入发送到两个输出的块,并且输出将是TransformBlock
s,如您所建议的。
public class SplitBlock<TInput, TOutputLeft, TOutputRight>
{
private ActionBlock<TInput> input;
private TransformBlock<TInput, TOutputLeft> leftOutput;
private TransformBlock<TInput, TOutputRight> rightOutput;
public ITargetBlock<TInput> Input { get { return input; } }
public ISourceBlock<TOutputLeft> LeftOutput { get { return leftOutput; } }
public ISourceBlock<TOutputRight> RightOutput { get { return rightOutput; } }
public SplitBlock(
Func<TInput, TOutputLeft> leftTransform,
Func<TInput, TOutputRight> rightTransform)
{
input = new ActionBlock<TInput>(
x =>
{
leftOutput.Post(x);
rightOutput.Post(x);
});
leftOutput = new TransformBlock<TInput, TOutputLeft>(leftTransform);
rightOutput = new TransformBlock<TInput, TOutputRight>(rightTransform);
// TODO handle fault in input correctly
input.Completion.ContinueWith(
_ =>
{
leftOutput.Complete();
rightOutput.Complete();
});
}
}
(这假设左右变换可以同时处理相同的输入。)
另一方面,如果您想在输入块中执行处理(这对我来说更有意义),那么您可以将ActionBlock
输入和BufferBlock
s 作为输出,输入块处理输入,然后将结果发送到输出块:
public class SplitBlock<TInput, TOutputLeft, TOutputRight>
{
private ActionBlock<TInput> input;
private BufferBlock<TOutputLeft> leftOutput;
private BufferBlock<TOutputRight> rightOutput;
public ITargetBlock<TInput> Input { get { return input; } }
public ISourceBlock<TOutputLeft> LeftOutput { get { return leftOutput; } }
public ISourceBlock<TOutputRight> RightOutput { get { return rightOutput; } }
public SplitBlock(
Func<TInput, Tuple<TOutputLeft, TOutputRight>> combinedTransform)
{
input = new ActionBlock<TInput>(
value =>
{
var result = combinedTransform(value);
leftOutput.Post(result.Item1);
rightOutput.Post(result.Item2);
});
leftOutput = new BufferBlock<TOutputLeft>();
rightOutput = new BufferBlock<TOutputRight>();
// TODO handle fault in input correctly
input.Completion.ContinueWith(
_ =>
{
leftOutput.Complete();
rightOutput.Complete();
});
}
public SplitBlock(
Func<TInput, TOutputLeft> leftTransform,
Func<TInput, TOutputRight> rightTransform)
: this(x => Tuple.Create(leftTransform(x), rightTransform(x)))
{}
}