3

TPL データフローが大好きです。

興味深い設計上の選択は、ほとんどの定義済みブロックが Delegates を使用して、処理ロジックを実装できるようにすることです。これは、単純なシナリオでは良さそうです。しかし、モジュール性とカプセル化を必要とする現実世界の大きなアプリケーションについて考えてみましょう。DELEGATE アプローチを使用して適切に構造化されたアプリを作成するのは難しく、不自然であることがわかりました。

たとえば、再利用可能なクラス TYPE (インスタンスではない) として aMultiplyIntByTwoTransformBlockと aだけが必要な場合。NoOpActionBlockどうすれば達成できますか?TransformBlock/から継承して、これを実現するためにいくつかのメソッドをActionBlockオーバーライドできるといいのですが。Process()ただし、事前定義されたブロックは封印されています。代理人のみを受け入れます。

カスタム ブロックを最初から作成できることはわかっていますが、必要なのは事前定義されたブロックに加えて少しカスタマイズするだけなので、明らかに複雑すぎます。

では、どうすれば目標を達成できるでしょうか。

更新: デリゲートにできないことがあると言っているわけではありません。多くのシナリオでは、テンプレート メソッド パターンで抽象ブロックを公開する方が優れていると言っています。たとえば、ポリモーフィズムを利用して、AbstractMultiplyBlock と MultiplyByTwoBlock と MultiplyByThreeBlock を記述できればいいのにと思います。残念ながら、デリゲートはそのような種類のデータとロジックの再利用性を提供しません。

4

4 に答える 4

3

現在、この問題を解決するために特別に設計されたオープン ソース ライブラリDataflowExがあります。さらに、データフロー グラフの作成と表示に役立つ機能がさらに提供されます。

免責事項: 私は DataflowEx の作成者です。私自身の質問に答えるために作成されました。他の人にも役立つことを願っています:)

于 2014-12-19T06:24:17.550 に答える
1

ターゲット ブロック型と同じインターフェイスを実装する抽象型を作成できます ( TransformBlockimplementsIPropagatorBlockおよび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 に答える