I am trying to create a general method for disposing an object that implements IDisposable, called DisposeObject()
To make sure I am disposing an object pointed by original reference, I am trying to pass an object by reference.
But I am getting a compilation error that says
The 'ref' argument type doesn't match parameter type
In the below (simplified) code, both _Baz
and _Bar
implement IDisposable.
So the questions are,
- Why am I getting this error?
- Is there a way to get around it?
[UPDATE]
From provided answers so far, as long as I do not set an IDisposable argument to null, I can simply pass an object by value without using ref
.
I am now having another trouble whether to set disposable objects to null
or not within DisposeObject
method.
Here is the full source for completeness:
public class Foo : IDisposable
{
private Bar _Bar;
private Baz _Baz;
private bool _IsDisposed;
~Foo() { Dispose(false); }
public void Dispose(bool disposing)
{
if (!_IsDisposed)
{
if (disposing)
{
DisposeObject(ref _Baz);
DisposeObject(ref _Bar);
}
}
_IsDisposed = true;
}
private void DisposeObject(ref IDisposable obj)
{
try
{
if (obj == null)
return;
obj.Dispose();
obj = null;
} catch (ObjectDisposedException) { /* Already Disposed... */ }
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
public class Bar : IDisposable
{
public void Dispose() {}
}
public class Baz : IDisposable
{
public void Dispose() {}
}
[RESULT]
I removed the code that sets argument to null (obj = null;
) within DisposeObject
So the final code became.
public void Dispose(bool disposing)
{
if (!_IsDisposed)
{
if (disposing)
{
DisposeObject(_Baz);
DisposeObject(_Bar);
}
}
_IsDisposed = true;
}
private void DisposeObject(IDisposable obj)
{
try
{
if (obj == null)
return;
obj.Dispose();
} catch (ObjectDisposedException) { /* Already Disposed... */ }
}
There is no need for you to pass by reference, as you're passing a reference type. You should remove the ref
keyword from your method definition. Do this and you shouldn't have any issues, though I'm not sure how this is more effective or clearer than simply calling Dispose()
(other than the fact that you don't have to cast it for explicit implementations and this does a null
check for you).
Edit
Dance, while I hope the discussion that's surrounded this topic has been helpful to you, your original intent doesn't seem to be something that's doable. In order to pass something as ref
, you can't pass a variable that has a type other than what the ref
parameter expects (in other words, you can't pass a variable declared as a class
or other interface
that implements IDisposable
if the ref
parameter is IDisposable
). Because ref
parameters allow assignments to propagate back to the caller, you would open up the possibility of allowing incompatible types being stored in your variable.
Your best bet here is to assign null
yourself if that's what you want. If you want to encapsulate the null
check and ignoring exceptions in to the function that's fine, but ref
will not work for you in this scenario no matter how you slice it, unfortunately.
Here's an option for your example (can't verify it against a compiler right now, but you'll get the idea):
private void DisposeObject<T>(ref T obj) where T : IDisposable
{
// same implementation
}
To call it, use
DisposeObject<Baz>(ref _Baz);
DisposeObject<Bar>(ref _Bar);
As pointed in the other comments, the compiler error you get has its own purpose (preventing you to assign some other type of IDisposable inside your method, leading to an inconsistent state).
Try this:
IDisposable d = (IDisposable)_Baz;
DisposeObject(ref d);
Edit:
As Adam points out, your code doesn't require this to be ref. Objects are always passed as references.
This approach smells funny but I'll ignore that for now.
To fix your problem, you need to cast the objects you are passing with "(IDisposable)"
I concede to the will of the compiler, and Jon Skeet. You need an actual object for this:
IDisposable _BazD = (IDisposable)_Baz;
DisposeObject(ref _BazD);
I'd also add a null check in your DisposeObject() in addition to the try/catch. The "obj==null" will be a quick and easy check when compared to expensive exception catching should this get hit multiple times for the same object. Hmm...was that there a minute ago? Nevermind.
Thank you Dan C. I don't have enough rep yet to add comments, so I have to add this as an answer. However, full credit to Dan C for this solution.
This is working code:
public override void Dispose()
{
base.Dispose();
DisposeOf<UserTableAdapter>(ref userAdapter);
DisposeOf<ProductsTableAdapter>(ref productsAdapter);
if (connection != null)
{
if (connection.State == ConnectionState.Open)
{
connection.Close();
}
DisposeOf<SqlConnection>(ref connection);
}
}
private void DisposeOf<T>(ref T objectToDispose) where T : IDisposable
{
if (objectToDispose != null)
{
objectToDispose.Dispose();
objectToDispose = default(T);
}
}