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

Observable.Defer - нужно уточнить, что именно он делает

Скажем, я хочу создать асинхронный поток случайных чисел, который выкачивает новое значение каждые 100 миллисекунд. Пытаясь придумать решение, моя первая попытка выглядела примерно так:

        var random = new Random();
        Observable.Start(() => random.Next())
                  .Delay(TimeSpan.FromMilliseconds(100))
                  .Repeat()
                  .Subscribe(Console.WriteLine);

Если вы попытаетесь запустить это, вы заметите, что он постоянно повторяет одно и то же значение снова и снова. Хорошо, я думаю, я неправильно понял, как работает Repeat. Поиграв немного, я придумал это, и это сработало:

        var random = new Random();
        Observable.Defer(()=> Observable.Start(() => random.Next()))
                  .Delay(TimeSpan.FromMilliseconds(100))
                  .Repeat()
                  .Subscribe(Console.WriteLine);

Итак, я пошел в документацию MSDN, чтобы понять, что делает Дефер, и вот что он говорит:

Возвращает наблюдаемую последовательность, которая вызывает наблюдаемый factoryкаждый раз, когда подписывается новый наблюдатель.

Я предполагаю, что моя путаница такова: в моем примере кода я только когда-либо подписываюсь на Observable один раз, так почему это, похоже, снова и снова вызывает Observable.Start(...)? Или я недопонимаю Repeat()? Любое уточнение было бы удивительным.

4b9b3361

Ответ 1

Вы недопонимаете повторение. В принципе, Repeat будет сохранять повторную подписку на наблюдаемую при каждом ее завершении.

Observable.Start, по-видимому, кэширует значение при первом вызове Subscribe и возвращает его каждый раз, когда вы подписываетесь. Это то, что вызывает одно и то же число снова и снова в первом примере.

Defer работает примерно так:

IObservable<T> Defer(Func<IObservable<T>> factory)
{
    return Observable.Create<T>(obs => factory().Subscribe(obs));
}

поэтому каждый раз, когда вы подписываетесь на наблюдаемый Defer, он должен вызывать функцию factory. В этом случае factory создает новый наблюдаемый, что позволяет избежать поведения кэширования Start.

Альтернативный способ получить описанную вами последовательность - использовать Interval для получения времени и Select для получения случайных чисел.

Observable.Interval(TimeSpan.FromMilliseconds(100))
          .Select(i => random.Next())
          .Subscribe(Console.WriteLine);

Ответ 2

Простым способом "генерации" случайных чисел является использование .Generate. Попробуйте следующее:

var rnd = new Random();

Observable
    .Generate(
        0,
        x => true,
        x => x,
        x => rnd.Next(),
        x => TimeSpan.FromMilliseconds(100.0))
    .Subscribe(n => { });