I love TPL dataflow.
Well, an interesting design choice is that, most predefined block use Delegates to allow us to implement processing logic. That looks good in simple scenarios. But let's think about real world big applications, which requires modularity and encapsulation. I found it hard and unnatural to write well structured app with the DELEGATE approach.
For example, if what I want is just a MultiplyIntByTwoTransformBlock and a NoOpActionBlock as a reusable class TYPE (not an instance). How do I achieve it? I wish I can inherit from TransformBlock/ActionBlock and say, override some Process() method to achieve this. But predefined blocks are sealed. They only accept delegates.
I know I can create a custom block from scratch but obviously it is too complex for me because what I need is just a little customization on top of the predefined ones.
So, how do I achieve my goal?
UPDATE: I am not saying there are things delegates cannot do. I am saying exposing abstract blocks in template method pattern is better in many scenarios. Say, I wish I can write a AbstractMultiplyBlock and MultiplyByTwoBlock and MultiplyByThreeBlock, taking advantage of polymorphism. Delegates, unfortunately does not provide such kind of data&logic reusability.
I don't see any reason why you would need custom block types for that. Helper methods should be enough:
public static IPropagatorBlock<int, int> CreateMultiplyIntTransformBlock(
int multiplier)
{
return new TransformBlock<int, int>(i => i * multiplier);
}
public static IPropagatorBlock<int, int> CreateMultiplyIntByTwoTransformBlock()
{
return CreateMultiplyIntTransformBlock(2);
}
If you think that a delegate isn't enough for you, then maybe you're trying to put your logic in the wrong place. There is no reason why the delegate can't use objects that use encapsulation and modularity properly. This way, your application logic stays separate from the logic of executing the code.
But if you really wanted to do what you're asking about, you could do that by encapsulating a TransformBlock in a custom class that implements IPropgatorBlock and also has your abstract Process() method. But doing that properly is somewhat complicated, have a look at Guide to Implementing Custom TPL Dataflow Blocks for details.
Now there is a open source library, DataflowEx, which is particularliy designed to solve this problem. In addition, it provides more features to help construct and represent a dataflow graph.
Disclaimer: I am the author of DataflowEx. It is created to answer my own question. Hope it also helps others :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With