2013-03-13 4 views
11

Ho due flussi che segnalano quando alcune condizioni cambiano. Ho bisogno di un osservabile che sparerà true quando tutte le condizioni turno true. false quando uno di essi diventa false. Se alcune delle condizioni sono false e altre modifiche a false, non è necessario aumentare gli eventi.Combinazione di osservabili booleani

Ecco come lo sto facendo:

// Introducing current states 
private bool cond1 = false; 
private bool cond2 = false; 

void MyHandlingMethod(IObservable<bool> c1, IObservable<bool> c2) 
{ 
    c1.Subscribe(b => _state1 = b); 
    c2.Subscribe(b => _state2 = b); 

    var c3 = c1.Merge(c2).Select(_ => _cond1 && _cond2); 

    c3.Subscribe(b => /* some action goes here /*); 
    // ... 
} 

voglio sapere se è il modo giusto di risolvere il mio problema e se ci sono eventuali pitfals. Ad esempio, gli scatti di sottoscrizione c3 prima di c1 e c2 a causa della natura asincrona di rx.

risposta

11

Non v'è alcuna necessità di mantenere lo stato:

c3 = c1.CombineLatest(c2, (a, b) => a && b).DistinctUntilChanged() 
+1

Humbug - Ho guardato oltre le API per anni e non sono riuscito a trovarlo! Tuttavia, probabilmente si desidera anche 'DistinctUntilChanged'. –

+0

[My go-to RX API reference] (http://www.introtorx.com/content/v1.0.10621.0/12_CombiningSequences.html) - l'esempio CombineLatest è questa domanda esatta (!) – AlexFoxGill

+0

Non penso che questo sia completamente soddisfa i requisiti. Solo se tutti i valori sono falsi se la sequenza produce un valore falso, altrimenti rimani in silenzio. Se tutti i valori sono veri, premere un valore vero. Questo soln spingerà false se qualsiasi valore è falso. –

2

Onestamente, probabilmente sarei andare con l'approccio CombineLatest, ma nell'interesse di toccare le parti in qualche modo-incontaminate del quadro Rx ...

Anche se non è una misura perfetta, è possibile utilizzare Observable.When/Observable.And/Observable.Then modello:

var firstStream = new Subject<bool>(); 
var secondStream = new Subject<bool>(); 
var thirdStream = new Subject<bool>(); 
var fourthStream = new Subject<bool>(); 

var query = Observable.When(firstStream 
     .And(secondStream) 
     .And(thirdStream) 
     .And(fourthStream) 
     .Then((v1,v2,v3,v4) => v1 & v2 & v3 & v4)); 

using(query.Subscribe(Console.WriteLine)) 
{ 
    firstStream.OnNext(true); 
    secondStream.OnNext(true); 
    thirdStream.OnNext(true); 
    // output stream will fire after this statement 
    fourthStream.OnNext(true); 
    Console.ReadLine(); 
} 

Uno dei vantaggi di questo approccio è che si stanno creando eventi di output solo quando tutti gli stream hanno dati che si combinano in base alla clausola Then, ma si legge anche piuttosto bene. Detto questo, ha un difetto fondamentale per il vostro caso d'uso: si necessario disporre di dati su ogni flusso in entrata per attivare il flusso di uscita:

using(query.Subscribe(Console.WriteLine)) 
{ 
    firstStream.OnNext(true); 
    secondStream.OnNext(true); 
    thirdStream.OnNext(true); 
    // output stream will fire after this statement 
    fourthStream.OnNext(true); 

    // this WON'T raise false on the output! 
    firstStream.OnNext(false); 
    secondStream.OnNext(false); 
    thirdStream.OnNext(false); 
    // output stream will fire false after this statement 
    fourthStream.OnNext(false); 
    Console.ReadLine(); 
} 
+0

:) Stavo per farlo in questo modo ... non va bene ... aggiungerò una risposta. –

0

Come JerKimball, stavo per suggerire utilizzando join (ora quando) :

IObservable<bool> cond1 = new [] { false, true, true, false, false }.ToObservable(); 
IObservable<bool> cond2 = new [] { true, true, false, false, true }.ToObservable(); 
Func<bool, bool> isFalse = (x) => x == false; 
Func<bool, bool> isTrue = (x) => x == true; 
var trueC1s = cond1.Where(isTrue); 
var falseC1s = cond1.Where(isFalse); 
var trueC2s = cond2.Where(isTrue); 
var falseC2s = cond2.Where(isFalse); 
var trues = trueC1s.And(trueC2s).Then((_, __) => true); 
var falses = trueC1s.And(falseC2s).Then((_, __) => false); 
var falses2 = falseC1s.And(trueC2s).Then((_, __) => false); 
var falses3 = falseC1s.And(falseC2s).Then((_, __) => false); 
Observable.When(trues, falses, falses2, falses3).Dump(); 

È un po 'caotico quando ci sono molti osservabili, comunque.

+0

Nice - true, è complicato, ma se lo dividete in singole istruzioni di 'Plan' come avete fatto qui, potete effettivamente" costruire "la query tramite un costrutto di looping. :) – JerKimball

+0

@JerKimball True :). –