Unless Disposed
Safety for lazy people, courtesy of PostSharp.
Note: this is an older post transferred over.
I’m just all about murdering the boilerplate recently.
This PostSharp aspect implements the “do not allow any public methods to be called after the object has been disposed” pattern for you, thereby trimming an arbitrarily large number of once-a-method:
if (disposed) throw new ObjectDisposedException (this.GetType().FullName) ;out of your code and replacing it with one simple
[UnlessDisposedAspect]on the class.
| namespace ArkaneSystems.Arkane.Aspects | |
| { | |
| /// <summary> | |
| /// An aspect which blocks calls to all public methods of a class implementing <see cref="IDisposable"/> once the class has | |
| /// been disposed, instead throwing <see cref="ObjectDisposedException"/>. The class upon which this aspect is placed must | |
| /// use the common pattern in which a private boolean field named 'disposed' is used to hold the disposed state. | |
| /// </summary> | |
| /// <remarks> | |
| /// Private methods, static methods, and finalizers can still run when the class is disposed, as they may | |
| /// be required for cleanup. | |
| /// </remarks> | |
| [ProvideAspectRole (StandardRoles.ExceptionHandling)] | |
| [MulticastAttributeUsage (MulticastTargets.Method)] | |
| [Serializable] | |
| public sealed class UnlessDisposedAspect : MethodInterceptionAspect | |
| { | |
| public override bool CompileTimeValidate (MethodBase method) | |
| { | |
| // Only validate if declaring type implements IDisposable. | |
| var type = method.DeclaringType; | |
| if (type.GetInterface ("System.IDisposable") == null) | |
| { | |
| Message.Write (MessageLocation.Of (method.DeclaringType), | |
| SeverityType.Error, | |
| "UD000", | |
| "UnlessDisposedAspect can only be applied to types implementing System.IDisposable."); | |
| return false; | |
| } | |
| var disposedField = type.GetField ("disposed", | |
| BindingFlags.Instance | BindingFlags.NonPublic); | |
| if (disposedField == null) | |
| { | |
| Message.Write (MessageLocation.Of (method.DeclaringType), | |
| SeverityType.Error, | |
| "UD001", | |
| "UnlessDisposedAspect can only be applied to types containing a private 'disposed' field."); | |
| return false; | |
| } | |
| if (disposedField.FieldType != typeof (bool)) | |
| { | |
| Message.Write (MessageLocation.Of (method.DeclaringType), | |
| SeverityType.Error, | |
| "UD002", | |
| "UnlessDisposedAspect can only be applied to types containing a boolean 'disposed' field."); | |
| return false; | |
| } | |
| return base.CompileTimeValidate (method); | |
| } | |
| /// <summary> | |
| /// Method invoked <i>instead</i> of the method to which the aspect has been applied. | |
| /// </summary> | |
| /// <param name="args">Advice arguments.</param> | |
| public override void OnInvoke (MethodInterceptionArgs args) | |
| { | |
| // Let calls to private and/or static methods, or the constructor, or the finalizer, proceed immediately. | |
| if (args.Method.IsPrivate || args.Method.IsStatic || args.Method.IsConstructor || | |
| args.Method.Name == "Finalize") | |
| { | |
| args.Proceed (); | |
| return; | |
| } | |
| var hasBeenDisposed = (bool) args.Method.DeclaringType.GetField ("disposed", | |
| BindingFlags.Instance | | |
| BindingFlags.NonPublic) | |
| .GetValue (args.Instance); | |
| if (hasBeenDisposed) | |
| throw new ObjectDisposedException (args.Instance.GetType ().FullName); | |
| args.Proceed (); | |
| } | |
| } | |
| } |
