通过引用传递IDisposable对象会导致错误?

时间:2009-04-27 15:47:24

标签: c# dispose idisposable pass-by-reference

我正在尝试创建一个处理实现IDisposable的对象的通用方法,称为DisposeObject()

为了确保我处理原始引用指向的对象,我试图通过引用传递一个对象。

但是我收到的编译错误是

  

'ref'参数类型与参数类型

不匹配

在下面(简化)的代码中,_Baz_Bar都实施IDisposable

alt text

所以问题是,

  1. 为什么我收到此错误?
  2. 有没有办法绕过它?
  3. [UPDATE] 从目前为止提供的答案,只要我没有将IDisposable参数设置为null,我就可以简单地按值传递一个对象而不使用ref。 我现在还有另一个麻烦,是否在null方法内将一次性对象设置为DisposeObject

    以下是完整性的完整来源:

    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() {}
    }
    

    [结果]
    我在obj = null;中删除了将参数设置为null(DisposeObject)的代码 所以最终的代码变成了。

        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 个答案:

答案 0 :(得分:5)

这是您的示例的一个选项(目前无法针对编译器进行验证,但您会明白这一点):

private void DisposeObject<T>(ref T obj) where T : IDisposable
{
    // same implementation
}

要拨打电话,请使用

DisposeObject<Baz>(ref _Baz);
DisposeObject<Bar>(ref _Bar);

正如其他注释中所指出的,你得到的编译器错误有其自己的目的(阻止你在方法中分配一些其他类型的IDisposable,导致状态不一致)。

答案 1 :(得分:4)

试试这个:

IDisposable d = (IDisposable)_Baz;
DisposeObject(ref d);

修改: 正如Adam指出的那样,您的代码不需要这样做。对象总是作为引用传递。

答案 2 :(得分:4)

当您传递引用类型时,无需通过引用传递。您应该从方法定义中删除ref关键字。这样做,你不应该有任何问题,虽然我不确定这比仅仅调用Dispose()更有效或更清楚(除了你不必为明确的实现投射它的事实)这会对你进行null检查。

修改

跳舞,虽然我希望围绕这个主题的讨论对你有所帮助,但你的原始意图似乎并不可行。为了传递ref的内容,你不能传递一个类型不同于ref参数所期望的类型的变量(换句话说,你不能传递一个声明为{的变量如果class参数为interface,{1}}或其他IDisposable实施ref。由于IDisposable参数允许赋值传播回调用者,因此可以允许将不兼容的类型存储在变量中。

这里最好的选择是自己指定ref,如果这就是你想要的。如果你想将null检查和忽略异常封装到很好的函数中,但不幸的是,无论你如何对它进行切片,null都不适合你。

答案 3 :(得分:2)

这种方法闻起来很有趣但我暂时忽略了这一点。

要解决您的问题,您需要使用“(IDisposable)”投射您传递的对象

我承认编译器和Jon Skeet的意愿。你需要一个实际的对象:

IDisposable _BazD = (IDisposable)_Baz;
DisposeObject(ref _BazD);

除了try / catch之外,我还会在DisposeObject()中添加一个空检查。与昂贵的异常捕获相比,“obj == null”将是一个快速简单的检查,如果同一个对象多次被击中,那么它就会被快速轻松地检查。嗯...那是一分钟之前的那个?没关系。

答案 4 :(得分:0)

谢谢Dan C.我还没有足够的代表添加评论,所以我必须添加这个作为答案。但是,Dan C对此解决方案完全赞同。

这是有效的代码:

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);
    }
}