Подтвердить что ты не робот

Пропустить элемент в потоке данных TransformBlock

TPL Dataflow предоставляет TransformBlock для преобразования ввода, например:

var tb = new TransformBlock<int, int>(i => i * 2);

Можно ли не выводить некоторые из входных данных, например. если на входе произошел некорректный тест проверки?

var tb = new TransformBlock<InputType, OutputType>(i =>
{
    if (!ValidateInput(i))
    {
        // Do something to not output anything for this input
    }
    // Normal output
}

Если это невозможно, какова будет лучшая модель для достижения этой цели?
Что-то вроде следующего?

BufferBlock<OutputType> output = new BufferBlock<OutputType>();

var ab = new ActionBlock<InputType>(i =>
{
    if (ValidateInput(i)) 
    {
        output.Post(MyTransform(i));
    }
}
4b9b3361

Ответ 1

Существует несколько вариантов того, как это сделать:

  • Используйте TransformManyBlock, как предложил Джон, и возвратите коллекцию, содержащую 1 или 0 элементов.
  • Используйте TransformBlock с некоторым специальным значением, представляющим "нет значения" (например, null), а затем используйте LinkTo() с фильтром, чтобы удалить их. Вы также должны привязать TransformBlock к нулевому блоку (DataflowBlock.NullTarget<T>()) без фильтра, чтобы слить специальные значения.
  • Я бы рассматривал это как-то из-за взлома, но вы также можете использовать конструктор Task на основе TransformBlock: используйте Task.FromResult(), когда вы хотите что-то вернуть, и null т. Например:

    new TransformBlock<int, int>(i => i % 2 == 0 ? Task.FromResult(i * 2) : null)
    

Ответ 2

Я сам не использовал DataFlow, но я думаю, вы могли бы использовать TransformManyBlock и просто сделать каждый шаг возвратом либо пустой коллекции, либо одного элемента.

var tmb = new TransformManyBlock<InputType, OutputType>(i =>
{
    if (!ValidateInput(i))
    {
        return Enumerable.Empty<OutputType>();
    }
    ...
    // Or return new[] { outputValue };
    return Enumerable.Repeat(outputValue, 1);
});

Вы даже можете обобщить это на FilterBlock<T>, у которого есть предикат фильтра, и передает соответствующие совпадения (точно так же, как Where в LINQ). Первоначально вы можете реализовать это с помощью TransformManyBlock, как указано выше, но затем сделайте его более эффективным позже.

Ответ 3

Немного старый вопрос, вы хотите добавить некоторый опыт здесь: вы можете ввести BufferBlock вместо ActionBlock для ваших данных и используйте LinkTo метод расширения с помощью предикат условия, поэтому действительные значения перейдут к TransformBlock, а недопустимые будут проигнорированы. Чтобы отбросить их, вы можете просто использовать блок NullTarget, который просто игнорирует полученные данные. Таким образом, окончательный код может выглядеть так:

var input = new BufferBlock<int>();
var tb = new TransformBlock<int, int>(i => i * 2);
var output = new BufferBlock<int>();

// valid integers will pass to the transform
input.LinkTo(tb, i => ValidateInput(i));

// not valid will be discarded
input.LinkTo(DataflowBlock.NullTarget<int>());

// transformed data will come to the output
tb.LinkTo(output);

Также ссылка может быть скорректирована с помощью DataflowLinkOptions с другая перегрузка LinkTo.