Created
January 21, 2014 18:56
-
-
Save cerebrate/8545938 to your computer and use it in GitHub Desktop.
Automatically implement the "if disposed, throw exception" pattern.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 (); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment