3

我喜欢 TPL 数据流。

嗯,一个有趣的设计选择是,大多数预定义的块使用委托来允许我们实现处理逻辑。这在简单的场景中看起来不错。但是让我们想想现实世界的大型应用程序,它需要模块化和封装。我发现使用 DELEGATE 方法编写结构良好的应用程序既困难又不自然。

例如,如果我想要的只是 aMultiplyIntByTwoTransformBlock和 aNoOpActionBlock作为可重用的类 TYPE(不是实例)。我如何实现它?我希望我可以从TransformBlock/继承ActionBlock并说,重写一些Process()方法来实现这一点。但是预定义的块是密封的。他们只接受代表。

我知道我可以从头开始创建自定义块,但显然它对我来说太复杂了,因为我需要的只是在预定义的基础上进行一些自定义。

那么,我该如何实现我的目标呢?

更新:我并不是说有些事情代表不能做。我是说在许多场景中以模板方法模式公开抽象块会更好。说,我希望我可以利用多态性编写一个 AbstractMultiplyBlock 和 MultiplyByTwoBlock 和 MultiplyByThreeBlock。不幸的是,代表们没有提供这种数据和逻辑的可重用性。

4

4 回答 4

5

我看不出有什么理由需要自定义块类型。辅助方法应该足够了:

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);
}

如果您认为委托对您来说还不够,那么您可能正试图将您的逻辑放在错误的位置。委托没有理由不能正确使用封装和模块化的对象。这样,您的应用程序逻辑与执行代码的逻辑保持分离。

但是,如果您真的想做您所要求的事情,您可以通过将 a 封装TransformBlock在实现IPropgatorBlock并具有您的抽象Process()方法的自定义类中来做到这一点。但是正确地做到这一点有些复杂,请查看实现自定义 TPL 数据流块的指南以获取详细信息。

于 2013-05-15T09:36:37.040 回答
3

现在有一个开源库DataflowEx,它是专门为解决这个问题而设计的。此外,它还提供了更多功能来帮助构建和表示数据流图。

免责声明:我是 DataflowEx 的作者。创建它是为了回答我自己的问题。希望它也对其他人有所帮助:)

于 2014-12-19T06:24:17.550 回答
1

TransformBlock您可以创建一个抽象类型,它实现与目标块类型( implementsIPropagatorBlock和)相同的接口IReceivableSourceBlock

不要复制该块的行为,而是将所有方法调用委托给innerBlock该类型的一个。

public abstract class AbstractMultiplyBlock<TInput, TOutput>
    : IPropagatorBlock<TInput, TOutput>, IReceivableSourceBlock<TOutput>
{
    private readonly TransformBlock<TInput, TOutput> innerBlock;

    protected AbstractMultiplyBlock(TransformBlock<TInput, TOutput> innerBlock)
    {
        this.innerBlock = innerBlock;
    }

    // ... interface implementations omitted for brevity, see appendix
}

这个抽象类具有与该类相同的所有属性和方法TransformBlock。现在,创建将 的实例传递TransformBlock给基本构造函数的派生类型。

public sealed class MultiplyByTwoBlock : AbstractMultiplyBlock<int, int>
{
    public MultiplyByTwoBlock()
        : base(new TransformBlock<int, int>(x => x * 2))
    {
    }
}

public sealed class MultiplyByThreeBlock : AbstractMultiplyBlock<int, int>
{
    public MultiplyByThreeBlock()
        : base(new TransformBlock<int, int>(x => x * 3))
    {
    }
}

用法与任何其他实例相同TransformBlock

var calculator1 = new MultiplyByTwoBlock();
var calculator2 = new MultiplyByThreeBlock();
calculator1.LinkTo(calculator2);

// x = 10 * 2 * 3
calculator1.Post(10);
var x = calculator2.Receive();

附录

完整的源代码AbstractMultiplyBlock

public abstract class AbstractMultiplyBlock<TInput, TOutput>
    : IPropagatorBlock<TInput, TOutput>, IReceivableSourceBlock<TOutput>
{
    private readonly TransformBlock<TInput, TOutput> innerBlock;

    protected AbstractMultiplyBlock(TransformBlock<TInput, TOutput> innerBlock)
    {
        this.innerBlock = innerBlock;
    }

    public DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock<TInput> source,
        bool consumeToAccept)
    {
        return ((ITargetBlock<TInput>)innerBlock).OfferMessage(messageHeader, messageValue, source, consumeToAccept);
    }

    public void Complete()
    {
        innerBlock.Complete();
    }

    public void Fault(Exception exception)
    {
        ((IDataflowBlock)innerBlock).Fault(exception);
    }

    public Task Completion
    {
        get { return innerBlock.Completion; }
    }

    public IDisposable LinkTo(ITargetBlock<TOutput> target, DataflowLinkOptions linkOptions)
    {
        return innerBlock.LinkTo(target, linkOptions);
    }

    public TOutput ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target, out bool messageConsumed)
    {
        return ((ISourceBlock<TOutput>)innerBlock).ConsumeMessage(messageHeader, target, out messageConsumed);
    }

    public bool ReserveMessage(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target)
    {
        return ((ISourceBlock<TOutput>)innerBlock).ReserveMessage(messageHeader, target);
    }

    public void ReleaseReservation(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target)
    {
        ((ISourceBlock<TOutput>)innerBlock).ReleaseReservation(messageHeader, target);
    }

    public bool TryReceive(Predicate<TOutput> filter, out TOutput item)
    {
        return innerBlock.TryReceive(filter, out item);
    }

    public bool TryReceiveAll(out IList<TOutput> items)
    {
        return innerBlock.TryReceiveAll(out items);
    }
}
于 2014-11-27T15:05:32.333 回答
0

到目前为止,我的自定义块被简单地编写为一堆使用的工厂方法DataflowBlock.Encapsulate(如果甚至需要的话)。

因此,对于返回传递给它的相同项目的 TransformBlock 的简单扩展,它看起来像这样:

public static class MutatorBlock {
    public static TransformBlock<T, T> New<T>(Action<T> action, ExecutionDataflowBlockOptions options) {
        return new TransformBlock<T, T>(
            input => {
                action(input);
                return input;
            }, options);
    }
}

要实例化此类,请使用MutatorBlock.New(...)而不是new MutatorBlock(...),但除此之外,没有太大区别。

我想我想知道的是,您到底需要什么类型?继承是行不通的,当然,组合仍然有效。你能举个例子说明这是一个主要问题吗?

于 2013-05-16T08:26:58.927 回答