Реализация RAII в C # - PullRequest
       23

Реализация RAII в C #

7 голосов
/ 02 апреля 2012

У меня есть форма InfoPath, мне нужно условно отключить ее события OnChange. Поскольку невозможно связать обработчики событий после загрузки формы, я вынужден полагаться на глобальный счетчик, который указывает, должно ли выполняться событие OnChange. Внутри каждого события OnChange я проверяю, есть ли SuppressEventsCount == 0, прежде чем выполнять какие-либо действия. Чтобы подавить события во время выполнения той или иной функции, я просто устанавливаю SuppressEventsCount ++ и - снова при выходе из функции. Самая большая проблема в том, что это не исключение. Поэтому у меня появилась блестящая идея заключить счетчик SuppressEvents в класс, который реализует iDisposable

using(SuppressEvents s = new SuppressEvents()){
   // OnChange events fired here are ignored
} // OnChange events enabled again

Это работает, но все же не так идеально, как решение на С ++, которое вообще не требует использования директивы using.

Есть ли способ либо:

  1. Запустить деструктор или некоторую функцию в тот момент, когда объект выходит из области видимости, ИЛИ
  2. Запретить инициализацию объекта SuppressEvents вне директивы using полностью

Ответы [ 4 ]

7 голосов
/ 02 апреля 2012

Нет и нет. using - самое близкое к RAII значение (точнее, речь идет о выпуске ресурса, который следует за уничтожением объекта RAII-идиомы).

Чтобы ответить на ваши вопросы более прямо:

  1. IDisposable (и с расширением using) был создан именно потому, что в .NET это сделать невозможно.
  2. using - это синтаксический сахар, который компилируется как try / finally и требует только, чтобы объект был IDisposable, поэтому вы не можете различить использование внутри оператора using и вне его.
2 голосов
/ 02 апреля 2012

Что касается вопроса 2, возможно, можно обойти его, предоставив другой интерфейс для потребителей кода. Вместо предоставления открытого класса, который реализует IDisposable, и надеясь, что они обернут его в using, вы могли бы предоставить статический метод, который принимает функцию для выполнения в «подавленном» контексте:

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

    private class SuppressActions : IDisposable {
        ...
    }
}

Тогда потребители могут использовать это следующим образом:

EventSuppressor.Suppress(() => {
    // OnChange events fired here are ignored
}) // OnChange events enabled again

Конечно, вы должны выяснить, подходит ли этот дизайн, так как это приведет к дополнительным вызовам функций, классам, сгенерированным компилятором, замыканиям и т. Д.

1 голос
/ 15 августа 2012

Если вы можете перейти с C # на C ++. NET (все еще на 100% .NET, если скомпилировано с clr: safe), то вы можете использовать msclr :: auto_handle, который действует как интеллектуальный указатель, такой как auto_ptr и т. Д.

То, что в действительности происходит за кулисами в IL, - это набор команд try / fault, но все это совершенно невидимо для разработчика и пользователя.Весь процесс просто лучше ИМО.

1 голос
/ 02 апреля 2012

Чтобы ответить на два ваших вопроса:

  1. Нет, сборка мусора в .NET не является детерминированной по своей природе
  2. Нет, вы не можете, предложение using преобразуется в код типа блока try / finally, и в любом случае вы не можете обнаружить, что оно построено из какой-либо из этих двух конструкций, по сравнению с внешним
...