Postponing actions
Introduction
Often modifications to an object (setting properties or calling methods etc.) cause synchronization actions to be executed. For instance, adding a SortDescription to an ICollectionView object can cause the object to perform a sorting operation on the items it contains. When multiple modifications to an object are made, it is desirable to postpone these synchronization actions until all modifications are made.
This blog presents the PostponableAction class with which this kind of behavior can be implemented. This idea is based on and can be used as an implementation to the DeferRefresh method of the ICollectionView interface.
Table of contents
Implementation
The heart of the PostponableAction class is made up of the inner class PostponableActionHelper. The purpose of this class is to perform an action when the instance is disposed. This class is implemented as follows:
private class PostponableActionHelper : IDisposable { private Action _onDispose; private bool _disposed; public PostponableActionHelper(Action onDispose) { if (onDispose == null) { throw new ArgumentNullException("onDispose"); } _onDispose = onDispose; _disposed = false; } ~PostponableActionHelper() { Dispose(false); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (_disposed) { return; } _onDispose(); _disposed = true; } }
The PostponableAction is implemented as follows:
public class PostponableAction { private Action _action; private int _postponeLevel; private bool _mustExecute; private Object _lockObject = new Object(); public PostponableAction(Action action) { if (action == null) { throw new ArgumentNullException("action"); } _action = action; } public void Execute() { lock (_lockObject) { if (_postponeLevel > 0) { _mustExecute = true; return; } } _action(); } public IDisposable PostponeAction { get { lock (_lockObject) { _postponeLevel++; } return new PostponableActionHelper(OnDisposePostponableActionHelper); } } public bool IsPostponing { get { return _postponeLevel > 0; } } private void OnDisposePostponableActionHelper() { lock (_lockObject) { if (--_postponeLevel > 0 || !_mustExecute) { return; } _mustExecute = false; } _action(); } }
Some notes on the implementation of PostponableAction:
- The implementation is thread safe
- If
Execute()has not been called during the lifetime of aPostponeActionobject, the action will not be executed when this object is disposed. - If
Execute()has been called during the lifetime of aPostponeActionobject, the action will be executed once when the lastPostponeActionobject is disposed.
Usage
The following class gives a sample of the usage of PostponableAction:
public class Work { private PostponableAction _synchronize = new PostponableAction(SynchronizeImplementation); private void SynchronizeImplementation() { // Some time consuming operations } public void Synchronize() { _synchronize.Execute(); } public IDisposable PostponeSynchronize() { get { return _synchronize.PostponeAction; } } public void Operation1() { // Some operations followed by Synchronize(); } public void Operation2() { // Some operations followed by Synchronize(); } public void Operation3() { using (PostponeSynchronize) { Operation1(); Operation2(); } } }
When Operation3 is called, the synchronization executed only once when the using block is exited.
Download
The source for the PostponableAction class can be downloaded here.

devblog.NET | Отложенные действия в C# said:
Sep 01, 10 at 3:23 pm[...] прочтения заметки в блоге Эмиля Джонгериуса (Emiel Jongerius) "Postponing actions". Отличия, из-за которых я написал свой вариант, указаны [...]