В IObservable
последовательность (в Реактивных Расширениях для.NET), я хотел бы получить значение предыдущих и элементов тока так, чтобы я мог сравнить их. Я нашел пример онлайн подобным, ниже которого выполняет задачу:
sequence.Zip(sequence.Skip(1), (prev, cur) => new { Previous = prev, Current = cur })
Это хорошо работает за исключением того, что это оценивает последовательность дважды, которой я хотел бы избежать. Вы видите, что это оценивается дважды с этим кодом:
var debugSequence = sequence.Do(item => Debug.WriteLine("Retrieved an element from sequence"));
debugSequence.Zip(debugSequence.Skip(1), (prev, cur) => new { Previous = prev, Current = cur }).Subscribe();
Вывод показывает вдвое больше строк отладки, поскольку существуют элементы в последовательности.
Я понимаю, почему это происходит, но до сих пор я не нашел альтернативу, которая не оценивает последовательность дважды. Как я могу объединить предыдущее и текущее только с одной оценкой последовательности?
Двойное вычисление является индикатором наблюдаемой холодной . Вы можете превратить его в Горячий, используя .Publish ():
var pub = sequence.Publish();
pub.Zip(pub.Skip(1), (...
pub.Connect();
Оказывается, вы можете использовать переменную для хранения предыдущего значения, обращения к нему и переназначения его в цепочке IObservable
расширений. Это работает даже во вспомогательном методе. С помощью приведенного ниже кода я теперь могу вызвать CombineWithPrevious ()
в моем IObservable
, чтобы получить ссылку на предыдущее значение без повторной оценки последовательности.
public class ItemWithPrevious<T>
{
public T Previous;
public T Current;
}
public static class MyExtensions
{
public static IObservable<ItemWithPrevious<T>> CombineWithPrevious<T>(this IObservable<T> source)
{
var previous = default(T);
return source
.Select(t => new ItemWithPrevious<T> { Previous = previous, Current = t })
.Do(items => previous = items.Current);
}
}
Если вам нужно получить доступ только к предыдущему элементу во время подписки, это, вероятно, самый простой способ, который сработает. (Я уверен, что есть способ лучше, может быть, оператор буфера в IObservable? Документация на данный момент довольно скудна, поэтому я не могу вам сказать.)
EventArgs prev = null;
sequence.Subscribe(curr =>
{
if (prev != null)
{
// Previous and current element available here
}
prev = curr;
});
EventArgs - это просто замена типа аргумент вашего события.