Observable Not Null and Value Observable True

994 Views Asked by At

This is a bit of a difficult one to explain, first look at the code:

public static IObservable<bool> NotNullAnd<T>(IReadOnlyReactiveProperty<T> prop,
    Func<T, IObservable<bool>> andSelector)
{
    var notNull = prop.Select(l => l != null);

    var isExecuting = prop
        .Where(l => l != null)
        .SelectMany(l => andSelector(l));

    return notNull.CombineLatest(isExecuting, (x, y) => x && y);
}

This code seems to work but im not sure if this is the best way to do this.

Basically im looking for a way to check when an observable on an object triggers but that object could be null so I need to first check that. So the combination is check for when the property changes, if not null then listen for another property on the object... Difficult to explain but perhaps the tests might help explain:

private class Loader
{
    public ReactiveProperty<bool> IsExecuting
        = new ReactiveProperty<bool>();
}

[Test]
public void TestNotNullAnd()
{
    var loaderProp = new ReactiveProperty<Loader>();

    var isExecutingProp = NotNullAnd(loaderProp, l => l.IsExecuting)
        .ToReadOnlyReactiveProperty();

    var loader = new Loader();

    Assert.IsFalse(isExecutingProp.Value);

    loaderProp.Value = loader;
    Assert.IsFalse(isExecutingProp.Value);

    loaderProp.Value.IsExecuting.Value = true;
    Assert.IsTrue(isExecutingProp.Value);

    loaderProp.Value.IsExecuting.Value = false;
    Assert.IsFalse(isExecutingProp.Value);

    loaderProp.Value.IsExecuting.Value = true;
    Assert.IsTrue(isExecutingProp.Value);

    loaderProp.Value.IsExecuting.Value = false;
    Assert.IsFalse(isExecutingProp.Value);

    loaderProp.Value.IsExecuting.Value = true;
    Assert.IsTrue(isExecutingProp.Value);

    loaderProp.Value = null;
    Assert.IsFalse(isExecutingProp.Value);

    loaderProp.Value = loader;
    Assert.IsTrue(isExecutingProp.Value);
}

As mentioned, all these tests pass but im not sure if there is a better way of doing it, plus im concerned that im introducing a memory leak in here somewhere because im not disposing of the listen to "l.IsExecuting"

Im using the "UniRx" libary for Unity.

1

There are 1 best solutions below

3
On BEST ANSWER

Corrected answer: Ah, I see now that you want nulls from prop to emit falses. In this case, you can simply map your nulls to wrapped false that get flattened into the return stream:

public static IObservable<bool> NotNullAnd<T>(IReadOnlyReactiveProperty<T> prop,
    Func<T, IObservable<bool>> andSelector)
{
  return prop.SelectMany(l => l == null ? Observable.Return(false) : andSelector(l));
}

Obselete answer: The nulls are already filtered out of prop's values with Where(l => l != null), so constructing notNull and combining it into isExecuting is redundant. Furthermore, it is dangerous because notNull and isExecuting might not be perfectly in sync and you could flip some values unexpectedly, especially as you chain more operators.

The following should be enough:

public static IObservable<bool> NotNullAnd<T>(IReadOnlyReactiveProperty<T> prop,
    Func<T, IObservable<bool>> andSelector)
{
    return prop
        .Where(l => l != null)
        .SelectMany(l => andSelector(l));
}