Is a Subject in RX.Net always harmful?
Asked Answered
A

4

9

I was talking to a colleague who pointed me to the SO question about subjects being considered harmful. However, I have two cases where I have some non-deterministic code that does not seem reasonable any other way.

Non-standard event:

 event handler(class, result)
 {
   subject.OnNext(result);
 }

 public delegate void _handler
   ([MarshalAs(UnmanagedType.Interface), In] MyClass class, 
    [MarshalAs(UnmanagedType.Interface), In] ResultClass result)

Parallel Tasks (Non-Deterministic number of tasks all running in parallel, starting at different times):

 Task.Start(()=> ...).ContinueWith(prevTask => subject.OnNext(prevTask.result))

The subject is not exposed, only through an observable. Is there another route suggested that isnt a ton of boilerplate?

Alexina answered 26/5, 2014 at 3:22 Comment(6)
The short answer is no. However, both of these cases can be handled with built in functionality. IObservable<T> ToObservable(this Task<T> task) should handle your task continuation case, and Observable.FromEvent should handle the other. Are these methods too verbose? The both provide more functionality than a simply subject, including being lazy.Lengel
@ChristopherHarris Thanks, but I'm not sure the first works and I know the second doesn't. The first because I have a non-deterministic number of tasks, and the second because, as noted, this is not a standard event...which FromEvent does not work onAlexina
When you say "non-deterministic number of tasks all running in parallel", what you really mean is "multiple eagerly invoked tasks", no?Lengel
@ChristopherHarris yes. They can start at different times, or else a merge would workAlexina
What's the delegate type for your nonstandard event?Closeup
@BenAaronson Updated the code with the delegateAlexina
C
6

Subjects are not always harmful. There are many legitimate uses of them even within Rx itself. However, many times a person goes to use a Subject, there's already a robust Rx method written for that scenario(and it may or may not be using subjects internally). This is the case for your 2 examples. Look at Task.ToObservable and Observable.FromEventPattern.

Another common case subjects are misused is when a developer breaks a stream in two. They become convinced they need to subscribe to a stream and in the callback they produce data for a new stream. They do this with a Subject. But usually they just should have used Select instead.

Communicable answered 26/5, 2014 at 23:52 Comment(1)
See my comment to @ChristopherHarris above as both options do not work for meAlexina
L
1

Observable.FromEvent

System.FromEvent works for more than just built-in event types: you just need to use the correct overload.

class Program
{
    private static event Action<int> MyEvent;

    public static void Main(string[] args)
    {
        Observable.FromEvent<int>(
            (handler) => Program.MyEvent += handler,
            (handler) => Program.MyEvent -= handler
            )
            .Subscribe(Console.WriteLine);

        Program.MyEvent(5);

        Console.ReadLine();
    }
}

Task.ToObservable & Merge

If you already have access to all of your tasks, you can convert them to Observables, and Merge them into a single observable.

class Program
{
    public static void Main(string[] args)
    {
        Observable.Merge(
                // Async / Await
                (
                    (Func<Task<string>>)
                    (async () => { await Task.Delay(250); return "async await"; })
                )().ToObservable(),
                // FromResult
                Task.FromResult("FromResult").ToObservable(),
                // Run
                Task.Run(() => "Run").ToObservable()
            )
            .Subscribe(Console.WriteLine);

        Console.ReadLine();
    }
}

Merge Observable

Alternatively, if you do not have all of your tasks up front, you can still use Merge, but you'll need some way of communicating future tasks. In this case, I've used a subject, but you should use the simplest Observable possible to express this. If that's a subject, then by all means, use a subject.

class Program
{
    public static void Main(string[] args)
    {
        // We use a subject here since we don't have all of the tasks yet.
        var tasks = new Subject<Task<string>>();

        // Make up some tasks.
        var fromResult = Task.FromResult("FromResult");
        var run = Task.Run(() => "Run");
        Func<Task<string>> asyncAwait = async () => {
            await Task.Delay(250);
            return "async await";
        };

        // Merge any future Tasks into an observable, and subscribe.
        tasks.Merge().Subscribe(Console.WriteLine);

        // Send tasks.
        tasks.OnNext(fromResult);
        tasks.OnNext(run);
        tasks.OnNext(asyncAwait());

        Console.ReadLine();
    }
}

Subjects

Why to use or not to use Subjects is a question I don't have the time to answer adequately. Typically speaking, however, I find that using a Subject tends to be the "easy way out" when it appears an operator does not already exist.

If you can somehow limit the exposure of a subject in terms of it's visibility to the rest of the application, then by all means use a subject and do so. If you're looking for message bus functionality, however, you should rethink the design of the application, as message buses are anti-patterns.

Lengel answered 28/5, 2014 at 16:50 Comment(0)
S
1

Subjects aren't harmful. That is probably even a little too dogmatic for me (and I am first to boo-boo the use of subjects). I would say that Subjects indicate a code smell. You probably could be doing it better without them, but if you keep the encapsulated within your class then at least you keep the smell in one place.

Here I would say, that you are already using "non-standard" event patterns, and it seems you don't want to, or cant, change that. In this case, it seems the usage of subjects as a bridge isn't going to make it any worse than it is.

If you were starting from scratch, then I would suggest that you deeply think about your design and you will probably find that you just wouldn't need a subject.

Lastly, I agree with the other comments that you should be using a FromEvent and ToTask, but you suggest these do not work. Why? I dont think you provide nearly enough of your code base to help with design questions like this. e.g. How are thee nondeterministic task being created? and by what? What is the actual problem you are trying to solve. If you could provide a full example, you might get the amount of attention you are looking for.

Strophic answered 4/6, 2014 at 7:58 Comment(0)
D
0

Here is what a good book about the Rx says regarding why and when Subject can be harmful:

http://www.introtorx.com/Content/v1.0.10621.0/18_UsageGuidelines.html

"Avoid the use of the subject types. Rx is effectively a functional programming paradigm. Using subjects means we are now managing state, which is potentially mutating. Dealing with both mutating state and asynchronous programming at the same time is very hard to get right. Furthermore, many of the operators (extension methods) have been carefully written to ensure that correct and consistent lifetime of subscriptions and sequences is maintained; when you introduce subjects, you can break this. Future releases may also see significant performance degradation if you explicitly use subjects."

Dessert answered 18/9, 2018 at 7:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.