Skip to content

Defer

1. 概要

Defer は、Observable の生成を購読時まで遅延させるファクトリメソッドです。購読されるたびにファクトリ関数が呼び出され、新しい Observable が生成されます。

副作用を持つ Observable の生成を購読時まで遅延させたい場合や、購読のたびに最新の状態に基づいて Observable を構築したい場合に使用します。

2. シグネチャ

csharp
public static Observable<T> Defer<T>(Func<Observable<T>> observableFactory, bool rawObserver = false)

observableFactory は購読のたびに呼び出されます。各購読者は独立した Observable インスタンスを受け取ります。

rawObserver は通常 false のまま使います。true にすると observer.Wrap() による中継を省き、下流の Observer<T> を直接使います。詳しい違いは rawObserver を参照してください。

csharp
Observable.Defer(() => Observable.Return(DateTime.Now))

3. マーブルダイアグラム

Defer のマーブルダイアグラム

購読のたびにファクトリ関数が呼び出され、生成された Observable のシーケンスがそのまま転送されます。各購読者は独立したシーケンスを受け取ります。

4. サンプルコード

csharp
using R3;

// === 購読時に最新の値を取得 ===
// Defer なし: Observable 生成時の時刻が固定される
var eager = Observable.Return(DateTime.Now);

// Defer あり: 購読するたびに現在時刻を取得
var deferred = Observable.Defer(() => Observable.Return(DateTime.Now));

await Task.Delay(1000);

deferred.Subscribe(x => Console.WriteLine($"1 回目: {x:HH:mm:ss.fff}"));

await Task.Delay(1000);

deferred.Subscribe(x => Console.WriteLine($"2 回目: {x:HH:mm:ss.fff}"));
// 出力例: 1 回目: 12:00:01.000
// 出力例: 2 回目: 12:00:02.000(購読のたびに最新の時刻)

// === 副作用の遅延実行 ===
int counter = 0;

var deferred2 = Observable.Defer(() =>
{
    counter++;
    Console.WriteLine($"ファクトリ呼び出し #{counter}");
    return Observable.Return(counter);
});

// ファクトリはまだ呼ばれていない
Console.WriteLine($"購読前: counter = {counter}");
// 出力: 購読前: counter = 0

deferred2.Subscribe(x => Console.WriteLine($"値: {x}"));
// 出力: ファクトリ呼び出し #1
// 出力: 値: 1

deferred2.Subscribe(x => Console.WriteLine($"値: {x}"));
// 出力: ファクトリ呼び出し #2
// 出力: 値: 2

5. 補足

Create との違い

Create は値の発行ロジックそのものを Observer<T> 経由で記述します。Defer は既存の Observable ファクトリの実行を購読時まで遅延させるだけであり、内部の Observable に値の発行を委ねます。

値の生成ロジックを一から記述したい場合は Create を、既存の Observable 生成メソッドの実行を遅延させたい場合は Defer を使用してください。