Пропустить элемент в потоке данных 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));
}
}
Ответы
Ответ 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
.