Skip to content

Instantly share code, notes, and snippets.

@meklarian
Forked from njonsson/DisposerBase.cs
Created November 8, 2012 21:19
Show Gist options
  • Save meklarian/4041679 to your computer and use it in GitHub Desktop.
Save meklarian/4041679 to your computer and use it in GitHub Desktop.
using System;
using System.Diagnostics;
/// <summary>
/// Serves as a wrapper around objects that require disposal but that do not
/// implement <see cref="System.IDisposable"/>.
/// </summary>
/// <typeparam name="T">The type of <see cref="Object"/></typeparam>
public abstract class DisposerBase<T> : IDisposable
{
/// <summary>
/// Indicates that <see cref="Object"/> is disposed.
/// </summary>
protected bool IsDisposed;
/// <summary>
/// Instantiates a new <see cref="DisposerByReflection{T}"/> object with the
/// specified <paramref name="object"/>.
/// </summary>
/// <param name="object">The value of <see cref="Object"/>.</param>
protected DisposerBase(T @object)
{
Object = @object;
}
~DisposerBase()
{
Debug.WriteLine(string.Format("{0} object was not disposed.",
typeof (IDisposable).FullName),
string.Format("You should call Dispose() on this {0} instead of letting it be finalized.",
GetType().FullName));
// ReSharper disable HeuristicUnreachableCode
Dispose(false);
// ReSharper restore HeuristicUnreachableCode
}
/// <summary>
/// The object to be disposed.
/// </summary>
public T Object { get; protected set; }
/// <summary>
/// Releases all resources used by the <see cref="DisposerBase{T}"/>.
/// </summary>
/// <exception cref="ObjectDisposedException"><see cref="Object"/> is
/// already disposed.</exception>
public void Dispose()
{
GC.SuppressFinalize(this);
Dispose(true);
}
/// <summary>
/// Releases the unmanaged resources used by the
/// <see cref="DisposerBase{T}"/> and optionally releases the managed
/// resources.
/// </summary>
/// <param name="disposing">If <c>true</c>, releases both managed and
/// unmanaged resources, otherwise releases only unmanaged
/// resources.</param>
/// <exception cref="ObjectDisposedException"><see cref="Object"/> is
/// already disposed.</exception>
protected void Dispose(bool disposing)
{
if (IsDisposed) throw new ObjectDisposedException(GetType().FullName);
IsDisposed = true;
DisposeImpl(disposing);
}
/// <summary>
/// Releases the unmanaged resources used by the
/// <see cref="DisposerBase{T}"/> and optionally releases the managed
/// resources.
/// </summary>
/// <param name="disposing">If <c>true</c>, releases both managed and
/// unmanaged resources, otherwise releases only unmanaged
/// resources.</param>
protected abstract void DisposeImpl(bool disposing);
}
using System;
/// <summary>
/// Serves as a wrapper around objects that require disposal but that do not
/// implement <see cref="System.IDisposable"/>.
/// </summary>
/// <typeparam name="T">The type of <see cref="object"/></typeparam>
public class DisposerByDelegation<T> : DisposerBase<T>
{
/// <summary>
/// Invoked when <see cref="DisposerBase{T}.Dispose"/> is called.
/// </summary>
protected readonly Action<T> DisposeAction;
/// <summary>
/// Instantiates a new <see cref="DisposerByDelegation{T}"/> object with the
/// specified <paramref name="object"/>.
/// </summary>
/// <param name="object">The value of <see cref="object"/>.</param>
/// <param name="disposeAction">The value of
/// <see cref="DisposeAction"/>.</param>
/// <exception cref="ArgumentNullException"><paramref name="object"/> is
/// <c>null</c>.</exception>
public DisposerByDelegation(T @object, Action<T> disposeAction)
: base(@object)
{
DisposeAction = disposeAction;
}
/// <summary>
/// Releases the unmanaged resources used by the
/// <see cref="DisposerByDelegation{T}"/> and optionally releases the
/// managed resources.
/// </summary>
/// <param name="disposing">If <c>true</c>, releases both managed and
/// unmanaged resources, otherwise releases only unmanaged
/// resources.</param>
protected override void DisposeImpl(bool disposing)
{
if (disposing)
{
if (DisposeAction != null) DisposeAction(Object);
}
}
}
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Test
{
[TestClass]
public class DisposerByDelegationTest
{
protected class SomeObject
{
}
[TestMethod]
public void ShouldHaveTheExpectedObject()
{
SomeObject @object = null;
Assert.IsNull(new DisposerByDelegation<SomeObject>(@object,
null).Object);
@object = new SomeObject();
var disposer = new DisposerByDelegation<SomeObject>(@object, null);
Assert.AreSame(@object, disposer.Object);
}
[TestMethod]
public void ShouldDelegateTheDisposeMessageToDisposeAction()
{
var disposalCount = 0;
using (new DisposerByDelegation<SomeObject>(new SomeObject(),
o => disposalCount++))
{
}
Assert.AreEqual(1, disposalCount);
}
[TestMethod]
[ExpectedException(typeof (DivideByZeroException))]
public void ShouldBubbleDisposeActionExceptions()
{
using (new DisposerByDelegation<SomeObject>(new SomeObject(),
o => { throw new DivideByZeroException("Whoa, Nelly!"); }))
{
}
}
[TestMethod]
[ExpectedException(typeof (ObjectDisposedException))]
public void ShouldThrowObjectDisposedExceptionWhenSentDisposeTwice()
{
var @object = new SomeObject();
using (var disposer = new DisposerByDelegation<SomeObject>(@object,
null))
{
disposer.Dispose();
}
}
}
}
using System;
using System.Reflection;
/// <summary>
/// Serves as a wrapper around objects that require disposal but that do not
/// implement <see cref="System.IDisposable"/>.
/// </summary>
/// <typeparam name="T">The type of
/// <see cref="DisposerBase{T}.Object"/></typeparam>
public class DisposerByReflection<T> : DisposerBase<T>
{
private static MethodInfo GetPublicParameterlessInstanceMethod(T @object,
string methodName)
{
if (Equals(@object, null)) return null;
if (methodName == null) throw new ArgumentNullException("methodName");
const BindingFlags binding = BindingFlags.ExactBinding |
BindingFlags.Instance |
BindingFlags.Public;
var method = @object.GetType().GetMethod(methodName,
binding,
null,
CallingConventions.HasThis,
new Type[0],
null);
if (method == null)
{
throw new ArgumentException(string.Format("Object does not have a public, parameterless {0} instance method",
methodName));
}
return method;
}
/// <summary>
/// Instantiates a new <see cref="DisposerByReflection{T}"/> object with the
/// specified <paramref name="object"/> and a public, parameterless instance
/// <see cref="Method"/> named "Dispose".
/// </summary>
/// <param name="object">The value of
/// <see cref="DisposerBase{T}.Object"/>.</param>
/// <exception cref="ArgumentNullException"><paramref name="object"/> is
/// <c>null</c>.</exception>
/// <exception cref="ArgumentException"><paramref name="object"/> does not
/// have a public, parameterless instance method named
/// "Dispose".</exception>
public DisposerByReflection(T @object) : this(@object, "Dispose")
{
}
/// <summary>
/// Instantiates a new <see cref="DisposerByReflection{T}"/> object with the
/// specified <paramref name="object"/> and the specified public,
/// parameterless instance <see cref="Method"/> named
/// <paramref name="methodName"/>.
/// </summary>
/// <param name="object">The value of
/// <see cref="DisposerBase{T}.Object"/>.</param>
/// <param name="methodName">The name of <see cref="Method"/>; must be a
/// public, parameterless instance method.</param>
/// <exception cref="ArgumentNullException"><paramref name="object"/> or
/// <paramref name="methodName"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException"><paramref name="object"/> does not
/// have a public, parameterless instance method named
/// <paramref name="methodName"/>.</exception>
public DisposerByReflection(T @object, string methodName)
: this(@object,
GetPublicParameterlessInstanceMethod(@object, methodName))
{
}
/// <summary>
/// Instantiates a new <see cref="DisposerByReflection{T}"/> object with the
/// specified <paramref name="object"/> and <paramref name="method"/>.
/// </summary>
/// <param name="object">The value of
/// <see cref="DisposerBase{T}.Object"/>.</param>
/// <param name="method">The value of <see cref="Method"/>; must be a member
/// of <paramref name="object"/>.</param>
/// <exception cref="ArgumentNullException"><paramref name="object"/> or
/// <paramref name="method"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException"><paramref name="method"/> is
/// static.</exception>
public DisposerByReflection(T @object, MethodInfo method) : base(@object)
{
if (Equals(@object, null))
{
throw new ArgumentNullException("object");
}
if (method == null)
{
throw new ArgumentNullException("method");
}
if (method.IsStatic)
{
throw new ArgumentException("Can't be a static method", "method");
}
if (method.GetParameters().Length > 0)
{
throw new ArgumentException("Can't be a method with parameters",
"method");
}
if (method.ContainsGenericParameters)
{
throw new ArgumentException("Can't be a method with unassigned generic type parameters",
"method");
}
Method = method;
}
/// <summary>
/// The <see cref="System.Reflection.MethodInfo"/> to which
/// <see cref="DisposerBase{T}.Dispose"/> will be delegated.
/// </summary>
public MethodInfo Method { get; protected set; }
/// <summary>
/// Releases the unmanaged resources used by the
/// <see cref="DisposerByReflection{T}"/> and optionally releases the
/// managed resources.
/// </summary>
/// <param name="disposing">If <c>true</c>, releases both managed and
/// unmanaged resources, otherwise releases only unmanaged
/// resources.</param>
protected override void DisposeImpl(bool disposing)
{
if (disposing)
{
try
{
Method.Invoke(Object, null);
}
catch (TargetInvocationException e)
{
throw e.InnerException;
}
}
}
}
using System;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Test
{
[TestClass]
public class DisposerByReflectionTest
{
protected class WithCloseMethod
{
public void Close()
{
}
}
protected class WithDisposeMethod
{
public int DisposalCount;
public void Dispose()
{
DisposalCount++;
}
}
protected class WithDisposeMethodThatThrows
{
public void Dispose()
{
throw new DivideByZeroException("Whoa, Nelly!");
}
}
protected class WithGenericDisposeMethod
{
public void Dispose<T>()
{
}
}
protected class WithParameterizedDisposeMethod
{
public void Dispose(int foo)
{
}
}
protected class WithProtectedDisposeMethod
{
protected void Dispose()
{
}
}
protected class WithStaticDisposeMethod
{
public static void Dispose()
{
}
}
[TestMethod]
[ExpectedException(typeof (ArgumentNullException))]
public void ShouldThrowArgumentNullExceptionWhenConstructedWithNullObject()
{
new DisposerByReflection<WithDisposeMethod>(null);
}
[TestMethod]
[ExpectedException(typeof (ArgumentException))]
public void ShouldThrowExpectedArgumentExceptionWhenLackingDisposeMethod()
{
var withoutDisposeMethod = new WithCloseMethod();
new DisposerByReflection<WithCloseMethod>(withoutDisposeMethod);
}
[TestMethod]
[ExpectedException(typeof (ArgumentException))]
public void ShouldThrowExpectedArgumentExceptionWhenLackingInstanceDisposeMethod()
{
var withStaticDisposeMethod = new WithStaticDisposeMethod();
new DisposerByReflection<WithStaticDisposeMethod>(withStaticDisposeMethod);
}
[TestMethod]
[ExpectedException(typeof (ArgumentException))]
public void ShouldThrowExpectedArgumentExceptionWhenLackingPublicDisposeMethod()
{
var withProtectedDisposeMethod = new WithProtectedDisposeMethod();
new DisposerByReflection<WithProtectedDisposeMethod>(withProtectedDisposeMethod);
}
[TestMethod]
[ExpectedException(typeof (ArgumentException))]
public void ShouldThrowExpectedArgumentExceptionWhenLackingParameterlessDisposeMethod()
{
var withParameterizedDisposeMethod = new WithParameterizedDisposeMethod();
new DisposerByReflection<WithParameterizedDisposeMethod>(withParameterizedDisposeMethod);
}
[TestMethod]
[ExpectedException(typeof (ArgumentException))]
public void ShouldThrowExpectedArgumentExceptionWhenLackingSpecifiedMethodByName()
{
var withoutCloseMethod = new WithDisposeMethod();
new DisposerByReflection<WithDisposeMethod>(withoutCloseMethod,
"Close");
}
[TestMethod]
[ExpectedException(typeof (ArgumentNullException))]
public void ShouldThrowArgumentNullExceptionWhenConstructedWithNullMethodName()
{
var withDisposeMethod = new WithDisposeMethod();
new DisposerByReflection<WithDisposeMethod>(withDisposeMethod,
(string) null);
}
[TestMethod]
[ExpectedException(typeof (ArgumentNullException))]
public void ShouldThrowArgumentNullExceptionWhenConstructedWithNullMethod()
{
var withDisposeMethod = new WithDisposeMethod();
new DisposerByReflection<WithDisposeMethod>(withDisposeMethod,
(MethodInfo) null);
}
[TestMethod]
[ExpectedException(typeof (ArgumentException))]
public void ShouldThrowArgumentExceptionWhenConstructedWithAStaticMethod()
{
var withStaticDisposeMethod = new WithStaticDisposeMethod();
const BindingFlags binding = BindingFlags.Public |
BindingFlags.Static;
var staticMethod = withStaticDisposeMethod.GetType().GetMethod("Dispose",
binding);
new DisposerByReflection<WithStaticDisposeMethod>(withStaticDisposeMethod,
staticMethod);
}
[TestMethod]
[ExpectedException(typeof (ArgumentException))]
public void ShouldThrowArgumentExceptionWhenConstructedWithAParameterizedMethod()
{
var withParameterizedDisposeMethod = new WithParameterizedDisposeMethod();
var parameterizedMethod = withParameterizedDisposeMethod.GetType().GetMethod("Dispose");
new DisposerByReflection<WithParameterizedDisposeMethod>(withParameterizedDisposeMethod,
parameterizedMethod);
}
[TestMethod]
[ExpectedException(typeof (ArgumentException))]
public void ShouldThrowArgumentExceptionWhenConstructedWithAnUnboundGenericMethod()
{
var withGenericDisposeMethod = new WithGenericDisposeMethod();
var unboundGenericMethod = withGenericDisposeMethod.GetType().GetMethod("Dispose");
new DisposerByReflection<WithGenericDisposeMethod>(withGenericDisposeMethod,
unboundGenericMethod);
}
[TestMethod]
public void ShouldNotThrowWhenConstructedWithABoundGenericMethod()
{
var withGenericDisposeMethod = new WithGenericDisposeMethod();
var unboundGenericMethod = withGenericDisposeMethod.GetType().GetMethod("Dispose");
var boundGenericMethod = unboundGenericMethod.MakeGenericMethod(typeof (DateTime));
new DisposerByReflection<WithGenericDisposeMethod>(withGenericDisposeMethod,
boundGenericMethod);
}
[TestMethod]
public void ShouldHaveTheExpectedObject()
{
var expectedObject = new WithDisposeMethod();
var disposer = new DisposerByReflection<WithDisposeMethod>(expectedObject);
Assert.AreSame(expectedObject, disposer.Object);
}
[TestMethod]
public void ShouldHaveTheExpectedDisposeMethodWhenNotSpecified()
{
var withDisposeMethod = new WithDisposeMethod();
const BindingFlags binding = BindingFlags.ExactBinding |
BindingFlags.Instance |
BindingFlags.Public;
var expectedMethod = withDisposeMethod.GetType().GetMethod("Dispose",
binding,
null,
CallingConventions.HasThis,
Type.EmptyTypes,
null);
var disposer = new DisposerByReflection<WithDisposeMethod>(withDisposeMethod);
Assert.AreSame(expectedMethod, disposer.Method);
}
[TestMethod]
public void ShouldHaveTheExpectedMethodWhenSpecifiedByName()
{
var withCloseMethod = new WithCloseMethod();
const BindingFlags binding = BindingFlags.ExactBinding |
BindingFlags.Instance |
BindingFlags.Public;
var expectedMethod = withCloseMethod.GetType().GetMethod("Close",
binding,
null,
CallingConventions.HasThis,
Type.EmptyTypes,
null);
var disposer = new DisposerByReflection<WithCloseMethod>(withCloseMethod,
"Close");
Assert.AreSame(expectedMethod, disposer.Method);
}
[TestMethod]
public void ShouldHaveTheExpectedMethodWhenSpecified()
{
var withCloseMethod = new WithCloseMethod();
const BindingFlags binding = BindingFlags.ExactBinding |
BindingFlags.Instance |
BindingFlags.Public;
var expectedMethod = withCloseMethod.GetType().GetMethod("Close",
binding,
null,
CallingConventions.HasThis,
Type.EmptyTypes,
null);
var disposer = new DisposerByReflection<WithCloseMethod>(withCloseMethod,
expectedMethod);
Assert.AreSame(expectedMethod, disposer.Method);
}
[TestMethod]
[ExpectedException(typeof (TargetException))]
public void ShouldThrowExpectedTargetExceptionUponDisposalWhenLackingTheSpecifiedMethod()
{
var withoutCloseMethod = new WithDisposeMethod();
var method = typeof (WithCloseMethod).GetMethod("Close");
using (new DisposerByReflection<WithDisposeMethod>(withoutCloseMethod,
method))
{
}
}
[TestMethod]
public void ShouldDelegateTheDisposeMessageToTheObject()
{
var withDisposeMethod = new WithDisposeMethod();
using (new DisposerByReflection<WithDisposeMethod>(withDisposeMethod))
{
}
Assert.AreEqual(1, withDisposeMethod.DisposalCount);
}
[TestMethod]
[ExpectedException(typeof (DivideByZeroException))]
public void ShouldThrowWhenObjectThrowsUponDisposal()
{
var withDisposeMethodThatThrows = new WithDisposeMethodThatThrows();
using (new DisposerByReflection<WithDisposeMethodThatThrows>(withDisposeMethodThatThrows))
{
}
}
[TestMethod]
[ExpectedException(typeof (ObjectDisposedException))]
public void ShouldThrowObjectDisposedExceptionWhenSentDisposeTwice()
{
var withDisposeMethod = new WithDisposeMethod();
using (var disposer = new DisposerByReflection<WithDisposeMethod>(withDisposeMethod))
{
disposer.Dispose();
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment