在C#中实现RAII

Tre*_*ent 8 c# raii

我有一个InfoPath表单,我需要有条件地禁用它的OnChange事件.由于在表单加载后无法绑定事件处理程序,因此我不得不依赖一个全局计数器来指示是否应该执行OnChange事件.在每个OnChange事件中,我在执行任何操作之前检查SuppressEventsCount == 0.为了在执行某个函数或其他函数期间抑制事件,我只需设置SuppressEventsCount ++,并且 - 当函数退出时再次.这样做的最大问题是它不是例外.所以我有一个好主意将SuppressEvents计数器包装在一个实现iDisposable的类中

using(SuppressEvents s = new SuppressEvents()){
   // OnChange events fired here are ignored
} // OnChange events enabled again
Run Code Online (Sandbox Code Playgroud)

这是有效的,但它仍然不如c ++解决方案那么理想,它根本不需要使用"using"指令.

有没有办法:

  1. 在对象超出范围时触发析构函数或某些函数,或者
  2. 防止SuppressEvents对象完全在"using"指令之外进行初始化

Jon*_*Jon 7

不,不.using是最接近RAII的(更准确地说,我们正在讨论RAII-idiom对象被破坏后的资源释放).

更直接地回答你的观点:

  1. IDisposable(并且通过扩展using)完全是因为在.NET中无法做到这一点而创建的.
  2. using是语法糖,它被编译为try/ finally并且只需要对象IDisposable,因此你无法区分using语句中的用法和它之外的用法.


jef*_*ora 3

关于问题 2,可以通过向代码使用者提供不同的接口来解决这个问题。您可以提供一个静态方法,该方法采用在“抑制”上下文中执行的函数,而不是提供实现 的公共类,IDisposable并希望它们将其包装在 中:using

public static class EventSuppressor {
    public void Suppress(Action action) {
        using (var s = new SuppressActions()) {
            action();
        }
    }

    private class SuppressActions : IDisposable {
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

然后消费者可以如下使用:

EventSuppressor.Suppress(() => {
    // OnChange events fired here are ignored
}) // OnChange events enabled again
Run Code Online (Sandbox Code Playgroud)

当然,你必须弄清楚这种设计是否合适,因为这会导致额外的函数调用、编译器生成的类和闭包等。