3

I'm creating a helper to filter specific types of objects in a stream/observable. This is the gist:

public interface IGameAction { }

// Example action
public struct OpenDoorAction : IGameAction { }

// In Store.cs
public Subject<IGameAction> Actions;

public IObservable<T> When<T>() where T : IGameAction
{
    // Exception here
    return (IObservable<T>)this.Actions.Where(action => action is T);
}

When used like this:

this.When<OpenDoorAction>()
    .Subscribe(a => Debug.Log(a));

The following exception is thrown for the line marked above:

InvalidCastException: Specified cast is not valid.

I have tried using classes for either and both of IGameAction and the implementors (eg. OpenDoorAction). I don't understand why I need the cast to IObservable<T>, but without it the compiler says there is no implicit cast from IObservable<IGameAction> to IObservable<T>. Subject implements IObservable.

FWIW this is in Unity3D using UniRx

altschuler
  • 3,694
  • 2
  • 28
  • 55

1 Answers1

4

This is because there's no direct cast between IObservable<IGameAction> and the IObservable<T> even though you've specified the constraint.

You should instead re-cast the individual items in the stream:

return this.Actions.Where(action => action is T).Cast<T>();

And your return value will then be an IObservable of the correct type.


As mjwills points out in the comments, a helper function of IObservable<T>.OfType<T2> exists, which basically just does the where and cast for you.

Clint
  • 6,133
  • 2
  • 27
  • 48
  • It certainly should! That basically just does the `Where` and `Cast` for you. – Clint Dec 17 '17 at 20:20
  • 1
    Aha, that makes sense! I had to specify the source type as well, so `this.Actions.OfType()`. For the curious, `.Cast` does something to the effect of `.Select(a => (T)a)`, which is also a "solution" to the problem. Thanks! – altschuler Dec 17 '17 at 21:07