I am trying to create a general method for disposing an object that implements IDisposable, called DisposeObject()
我试图创建一个处理实现IDisposable的对象的通用方法,称为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
'ref'参数类型与参数类型不匹配
In the below (simplified) code, both _Baz
and _Bar
implement IDisposable.
在下面(简化)代码中,_Baz和_Bar都实现了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.
[更新]从目前为止提供的答案,只要我没有将IDisposable参数设置为null,我可以简单地通过值传递一个对象而不使用ref。我现在在DisposeObject方法中是否将一次性对象设置为null有另一个麻烦。
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.
[结果]我删除了在DisposeObject中将参数设置为null(obj = null;)的代码,因此最终代码变为。
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... */ }
}
5 个解决方案
#1
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).
您传递引用类型时无需通过引用传递。您应该从方法定义中删除ref关键字。这样做,你不应该有任何问题,虽然我不确定这比简单地调用Dispose()更有效或更清楚(除了你不必为明确的实现投射它的事实,这样做你的空检查)。
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.
跳舞,虽然我希望围绕这个话题的讨论对你有所帮助,你的原始意图似乎并不是可行的。为了传递某些内容作为ref,你不能传递一个变量,该变量的类型不是ref参数所期望的类型(换句话说,你不能传递声明为类的变量或实现IDisposable的其他接口,如果ref参数是IDisposable)。因为ref参数允许赋值传播回调用者,所以您可以开启允许不兼容类型存储在变量中的可能性。
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.
这里你最好的选择是自己分配null,如果这是你想要的。如果你想封装null检查并忽略异常函数,那么无论你如何对它进行切片,ref都不适合你。
#2
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).
正如其他注释中所指出的,您获得的编译器错误有其自己的目的(阻止您在方法中分配一些其他类型的IDisposable,从而导致状态不一致)。
#3
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.
编辑:正如亚当指出的那样,你的代码不需要这个参考。对象始终作为引用传递。
#4
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)"
要解决您的问题,您需要使用“(IDisposable)”转换您传递的对象
I concede to the will of the compiler, and Jon Skeet. You need an actual object for this:
我承认编译器和Jon Skeet的意愿。你需要一个实际的对象:
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.
除了try / catch之外,我还会在DisposeObject()中添加一个null检查。与昂贵的异常捕获相比,“obj == null”将是一个快速简单的检查,如果同一对象多次命中它。嗯...那是一分钟前的那个?没关系。
#5
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.
谢谢Dan C.我还没有足够的代表添加评论,所以我必须添加这个作为答案。但是,对于此解决方案,Dan C完全赞同。
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);
}
}
#1
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).
您传递引用类型时无需通过引用传递。您应该从方法定义中删除ref关键字。这样做,你不应该有任何问题,虽然我不确定这比简单地调用Dispose()更有效或更清楚(除了你不必为明确的实现投射它的事实,这样做你的空检查)。
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.
跳舞,虽然我希望围绕这个话题的讨论对你有所帮助,你的原始意图似乎并不是可行的。为了传递某些内容作为ref,你不能传递一个变量,该变量的类型不是ref参数所期望的类型(换句话说,你不能传递声明为类的变量或实现IDisposable的其他接口,如果ref参数是IDisposable)。因为ref参数允许赋值传播回调用者,所以您可以开启允许不兼容类型存储在变量中的可能性。
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.
这里你最好的选择是自己分配null,如果这是你想要的。如果你想封装null检查并忽略异常函数,那么无论你如何对它进行切片,ref都不适合你。
#2
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).
正如其他注释中所指出的,您获得的编译器错误有其自己的目的(阻止您在方法中分配一些其他类型的IDisposable,从而导致状态不一致)。
#3
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.
编辑:正如亚当指出的那样,你的代码不需要这个参考。对象始终作为引用传递。
#4
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)"
要解决您的问题,您需要使用“(IDisposable)”转换您传递的对象
I concede to the will of the compiler, and Jon Skeet. You need an actual object for this:
我承认编译器和Jon Skeet的意愿。你需要一个实际的对象:
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.
除了try / catch之外,我还会在DisposeObject()中添加一个null检查。与昂贵的异常捕获相比,“obj == null”将是一个快速简单的检查,如果同一对象多次命中它。嗯...那是一分钟前的那个?没关系。
#5
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.
谢谢Dan C.我还没有足够的代表添加评论,所以我必须添加这个作为答案。但是,对于此解决方案,Dan C完全赞同。
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);
}
}