Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Automatically implement the "if disposed, throw exception" pattern.
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