2016-07-03 33 views
5

Ho un gioco (in base a MonoGame/XNA) con un metodo di aggiornamento in questo modo:Come si trasforma un sistema di polling in un IObservable Rx.Net?

public void Update(GameTime gameTime) 
{ 
    component.Update(gameTime); 
} 

vorrei convertire questo il modello reattivo. La mia soluzione attuale è:

public void Initialize() 
{ 
    updateSubject = new Subject<GameTime>(); 

    component = new Component(); 

    updateSubject.Subscribe((gameTime) => component.Update(gameTime)); 
} 

public void Update(GameTime gameTime) 
{ 
    updateSubject.OnNext(gameTime); 
} 

Sono nuovo di Rx, quindi sto ancora imparando il modo migliore di fare le cose. Ho letto che lo Subject dovrebbe essere evitato e al suo posto dovrebbe essere usato Observable.Create.

È Subject appropriato qui?

In questo caso, come è possibile utilizzare Observable.Create?

risposta

6

Il problema chiave che stai affrontando qui è che hai bisogno di una fonte per il tuo osservabile. In generale è possibile creare osservabili da una varietà di fonti da eventi, delegati, attività, molte delle estensioni osservabili (come .Interval o .Generate) e soggetti.

Nel tuo caso devi avere una fonte che puoi avere codice, esterno ai tuoi valori osservabili, push a. In questo caso, un soggetto è perfettamente a posto, ma potresti anche usare un delegato.

Se si utilizza un oggetto, il codice va bene. L'unico lato negativo è che è possibile chiamare updateSubject.OnCompleted e terminare l'osservabile.

Se si desidera utilizzare un delegato, allora il codice potrebbe essere la seguente:

private Action<GameTime> updateGameTime = null; 

public void Initialize() 
{ 
    component = new Component(); 

    Observable 
     .FromEvent<GameTime>(a => updateGameTime += a, a => updateGameTime -= a) 
     .Subscribe((gameTime) => component.Update(gameTime)); 
} 

public void Update(GameTime gameTime) 
{ 
    updateGameTime(gameTime); 
} 

In questo modo l'unica cosa che si può fare con updateGameTime si passa in una nuova GameTime - non si può "accidentalmente "Termina la sequenza.

Ora l'intero problema con l'utilizzo di soggetti rispetto a Observable.Create è uno stato. Nel tuo codice hai bisogno di stato, quindi un soggetto è OK. In generale però, e quando possibile, è consigliabile incapsulare lo stato - e questo è ciò che Observable.Create fa per te.

Prendete questo esempio:

var i = -1; 
var query = 
    Observable 
     .Range(0, 10).Select(x => 
     { 
      i = -i * 2; 
      return x * i; 
     }); 

Se mi iscrivo a questo osservabile due volte mi vengono queste due sequenze:

(1)

 
0 
-4 
16 
-48 
128 
-320 
768 
-1792 
4096 
-9216 

(2)

 
0 
-4096 
16384 
-49152 
131072 
-327680 
786432 
-1835008 
4194304 
-9437184 

La sequenza cambia perché ho usato lo stato (ad es. var i = -1;).

Se avessi scritto il codice con Observable.Create ho potuto evitare questo stato:

var query = 
    Observable 
     .Create<int>(o => 
     { 
      var i = -1; 
      return 
       Observable 
       .Range(0, 10).Select(x => 
       { 
        i = -i * 2; 
        return x * i; 
       }) 
       .Subscribe(o); 
     }); 

E 'ancora la stessa query, ma lo Stato è incapsulato, quindi se mi iscrivo due volte ottengo:

(1)

 
0 
-4 
16 
-48 
128 
-320 
768 
-1792 
4096 
-9216 

(2)

 
0 
-4 
16 
-48 
128 
-320 
768 
-1792 
4096 
-9216 

Ci sono momenti in cui la scrittura di query complesse che si potrebbe pensare che l'utilizzo di un soggetto avrebbe reso molto più facile, e, in generale, è lì che si verificano errori. Dovresti sempre cercare di trovare un approccio puramente operatore prima di usare i soggetti in questo caso. Se non è possibile, incapsulare l'uso di un soggetto in Observable.Create.

volte è tutto come la vostra che l'utilizzo di un soggetto va bene perché è necessario che lo stato esterno.

0

solo sottolineando che si codifica unnessecarily usa Rx.

public void Initialize() 
{ 
    //updateSubject = new Subject<GameTime>(); 

    component = new Component(); 

    //updateSubject.Subscribe((gameTime) => component.Update(gameTime)); 
} 

public void Update(GameTime gameTime) 
{ 
    //updateSubject.OnNext(gameTime); 
    component.Update(gameTime) 
} 

Qui hanno tolto il Subject e chiamare direttamente attraverso il component s metodo Update, per illustrare il punto.

Forse siete alla ricerca di un metodo privato di polling? In tal caso Observable.Interval potrebbe essere un buon punto di partenza.

+1

ho dato un esempio minimo per SO. Il codice reale è più complesso di questo. – sdgfsdh

+0

È bello ascoltarlo. Ma se questo è un esempio completo e verificabile minimamente, allora il Soggetto (e quindi Rx) è ridondante. Non credo che tu fornisca informazioni sufficienti per fornire una risposta sensata. –

+1

Un esempio di dove è utile il mio 'IObservable ' è l'elaborazione dell'ingresso. Se ho anche un 'IObservable ' allora posso window usando il mio flusso 'GameTime'. – sdgfsdh