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

C# 通过引用传递IDisposable对象会导致错误吗?,c#,dispose,idisposable,pass-by-reference,C#,Dispose,Idisposable,Pass By Reference,我试图创建一个通用方法来处理实现的对象,称为DisposeObject() 为了确保我正在处理由原始引用指向的对象,我正在尝试通过引用传递对象 但是我得到了一个编译错误,它说 “ref”参数类型与参数类型不匹配 在下面的(简化)代码中,\u Baz和\u Bar都实现了。 所以问题是, 为什么我会犯这个错误 有办法绕过它吗 [更新] 从目前为止提供的答案来看,只要我没有将IDisposable参数设置为null,我就可以简单地按值传递对象,而无需使用ref。 我现在遇到了另一个问题:是否在Di

我试图创建一个通用方法来处理实现的对象,称为
DisposeObject()

为了确保我正在处理由原始引用指向的对象,我正在尝试通过引用传递对象

但是我得到了一个编译错误,它说

“ref”参数类型与参数类型不匹配

在下面的(简化)代码中,
\u Baz
\u Bar
都实现了。

所以问题是,

  • 为什么我会犯这个错误
  • 有办法绕过它吗
  • [更新] 从目前为止提供的答案来看,只要我没有将IDisposable参数设置为null,我就可以简单地按值传递对象,而无需使用
    ref
    。 我现在遇到了另一个问题:是否在
    DisposeObject
    方法中将一次性对象设置为
    null

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

    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() {}
    }
    
    [结果]
    我删除了在
    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... */ }
        }
    
    试试这个:

    IDisposable d = (IDisposable)_Baz;
    DisposeObject(ref d);
    
    编辑
    正如Adam所指出的,您的代码不需要将其作为引用。对象总是作为引用传递。

    这种方法闻起来很有趣,但我现在将忽略它

    要解决问题,您需要强制转换要传递的对象“(IDisposable)”

    我向编辑者和Jon Skeet的意愿让步。您需要一个实际对象来执行此操作:

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

    除了try/catch之外,我还要在DisposeObject()中添加一个null检查。与昂贵的异常捕获相比,“obj==null”将是一个快速而简单的检查,如果同一对象多次命中该异常。嗯……刚才还在吗?无需担心。

    传递引用类型时,不需要按引用传递。您应该从方法定义中删除
    ref
    关键字。这样做就不会有任何问题,尽管我不确定这比简单地调用
    Dispose()
    (除了您不必为显式实现强制转换它,这会为您检查
    null
    )更有效或更清晰

    编辑

    跳舞吧,虽然我希望围绕这个话题的讨论对你有所帮助,但你的初衷似乎并不可行。为了将某个变量作为
    ref
    传递,不能传递类型不同于
    ref
    参数所期望的类型的变量(换句话说,如果
    ref
    参数为
    IDisposable
    ,则无法传递声明为实现
    IDisposable
    类或其他
    接口的变量)。因为
    ref
    参数允许分配传播回调用者,因此您可能会允许不兼容的类型存储在变量中


    如果你想要的话,最好是自己分配
    null
    。如果你想将
    null
    封装到函数中,检查并忽略异常是可以的,但是
    ref
    在这种情况下不管你如何分割它都不适用。很遗憾,这里有一个选项供你选择(现在无法根据编译器验证它,但您会明白的):

    私有无效处置对象(参考T obj),其中T:IDisposable
    {
    //相同的实现
    }
    
    要称呼它,请使用

    DisposeObject<Baz>(ref _Baz);
    DisposeObject<Bar>(ref _Bar);
    
    DisposeObject(参考文献);
    处置对象(参考条);
    

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

    谢谢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);
        }
    }
    
    public override void Dispose()
    {
    base.Dispose();
    DisposeOf(ref userAdapter);
    处置(参考产品适配器);
    if(连接!=null)
    {
    if(connection.State==ConnectionState.Open)
    {
    connection.Close();
    }
    处置(参考连接);
    }
    }
    私有无效处置(参考T objectToDispose),其中T:IDisposable
    {
    if(objectToDispose!=null)
    {
    objectToDispose.Dispose();
    objectToDispose=默认值(T);
    }
    }
    
    这实际上不起作用。您会收到一个错误,即“ref或out参数必须是可赋值变量”。@dance2die,否,变量是按值传递的。但是,此变量持有引用类型,因此引用本身(而不是对象的内容)是通过值传递的内容。这与您的其他线程在这方面类似。ref关键字提供给您的唯一功能区别是能够为该变量赋值并将其反映在调用代码中。您没有这样做,因此不需要ref关键字。@Jon B:Objects引用是通过值传递的。Objects未通过[ref]默认情况下:您不能为实际参数分配新的对象引用,也不能在调用函数中调用后看到该值。@danc2die:Brian完全正确。默认情况下,引用是按值传递的。对象本身根本没有传递。我不相信我的文章声称它们是……我当然希望它没有这样说。@J在B上:除非您想更改调用方变量的值,在这种情况下,通过引用传递参数是完全有意义的在我看来,混淆了这两个问题。仅仅理解引用类型变量的值是一个引用而不是一个对象更简单、更准确。在这一点上,这一切都很好且一致。当参数为
    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);
        }
    }