lontivero
lontivero

Reputation: 5275

How to unsubscribe an event from inside the event handler?

My code subscribes to an event and it needs to unsubscribe (Dispose) once the event has been handled. However this looks like a chicken-egg problem. Using rec doesn't work and I cannot find how to do it.

It there any well-konwn pattern to bypass this limitation?

let process = new Process()
let exitSubscription = process.Exited.Subscribe (
    fun evntArg ->
        exitSubscription.Dispose()     <---------- Compiler complains here
        // do more something here.
     )

Upvotes: 1

Views: 112

Answers (1)

Fyodor Soikin
Fyodor Soikin

Reputation: 80744

(you're saying in your question that rec doesn't work, but do not clarify why or how; so I'm going to ignore that part for this answer)

One way to do this is to declare the value "recursive" with the rec keyword. This will allow the value initialization code to reference the value itself - just like with a recursive function.

let rec exitSubscription : IDisposable = process.Exited.Subscribe (
    fun evntArg ->
        exitSubscription.Dispose()
        // do more something here.
     )

This will work, but will also produce a warning saying "this is a recursive value, and I can't tell if you're actually accessing it while it's being constructed, so you have to make sure that you don't, and if you do anyway, it'll be a runtime error".

Note that you also have to add a type signature to the variable, otherwise the compiler can't quite grok it and complains that it might not have a Dispose method.

Another way is to make the variable mutable and then mutate it:

let mutable exitSubscription : IDisposable = null
exitSubscription <- process.Exited.Subscribe (
    fun evntArg ->
        exitSubscription.Dispose()
        // do more something here.
     )

Here you have to use a type signature too (for the same reason), and you have to initialize it with null, because there is no such thing as an uninitialized variable.

Also, this is a tiny bit less safe, because the variable is mutable, and that's always a source of bugs. As long as you pinky-promise not to mutate it (beyond this initialization), you're ok.

But a "proper" way is to use an observable combinator to limit the observable to only one element. That way you don't have to unsubscribe explicitly, which is always more reliable:

#r "nuget: System.Reactive"

open System.Reactive.Linq

process.Exited.Take(1).Subscribe (
    fun evntArg ->
        // do more something here.
     )

Upvotes: 3

Related Questions