如何从事件处理程序中取消订阅事件?

How to unsubscribe an event from inside the event handler?

我的代码订阅了一个事件,它需要在处理完事件后取消订阅 (Dispose)。然而,这看起来像是先有鸡还是先有蛋的问题。使用 rec 不起作用,我找不到如何操作。

有什么好的模式可以绕过这个限制吗?

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

(你在问题中说 rec 不起作用,但没有说明原因或方式;所以我将忽略此答案的那部分)

实现此目的的一种方法 是使用 rec 关键字声明值“递归”。这将允许值初始化代码引用值本身 - 就像递归函数一样。

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

这会起作用,但也会产生一个警告说“这是一个递归值,我无法判断你是否在构造它时实际访问它,所以你必须确保你不这样做,如果你这样做,这将是一个运行时错误”。

请注意,您还必须向变量添加类型签名,否则编译器无法理解它并抱怨它可能没有 Dispose 方法。

另一种方法是使变量可变,然后对其进行变异:

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

这里你也必须使用类型签名(出于同样的原因),你必须用null初始化它,因为没有未初始化的变量。

此外,这有点不安全,因为变量是可变的,而这始终是错误的来源。只要你 pinky-promise 不改变它(超出这个初始化),你就没问题。

但是“正确”的方法是使用可观察组合器将可观察对象限制为仅一个元素。这样您就不必明确取消订阅,这样总是更可靠:

#r "nuget: System.Reactive"

open System.Reactive.Linq

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