【问题标题】:C# disposing objectsC# 处理对象
【发布时间】:2011-04-26 01:49:11
【问题描述】:

我知道当您有以下代码时,会在 StreamReader 对象上调用 Dispose() 方法:

//Sample 1
using (StreamReader sr1 = new StreamReader(@"C:\Data.txt"))
{
    string s1 = sr1.ReadToEnd();
    //Do something with s1...
}

但是如果你编写这样的代码(示例 2),Dispose() 方法也会被调用吗?

//Sample 2
StreamReader sr2 = new StreamReader(@"C:\Data.txt");
using (sr2)
{
    string s2 = sr2.ReadToEnd();
    //Do something with s2...
}

【问题讨论】:

    标签: c# dispose idisposable


    【解决方案1】:

    是的,当然。详细信息在第 8.13 节中。您的确切问题没有简明扼要的陈述,但是:

    形式的 using 语句

    using (expression) statement
    

    具有相同的三种可能的扩展,但在这种情况下,ResourceType 隐含地是表达式的编译时类型,资源变量在嵌入语句中是不可访问的,也不可见。

    提到的“三种可能的扩展”涵盖了同时声明变量的更常见情况。基本上重要的是它的行为方式相同,除了变量的范围。 Dispose 仍将被调用 - 否则根本没有必要添加 using 语句:)

    请注意,就块内的有效内容而言,两者相当等效,因为using 语句声明的变量是只读的。所以这是有效的:

    // Warning but no error
    MemoryStream ms = new MemoryStream();
    using (ms)
    {
        ms = null;
    }
    

    但这不是:

    using (MemoryStream ms = new MemoryStream())
    {
        // error CS1656: Cannot assign to 'ms' because it is a 'using variable'
        ms = null;
    }
    

    请注意,即使在有效的情况下,它也是用于处置的ms 的原始 值。编译器清楚地表明了这一点:

    警告 CS0728:可能不正确地分配给本地“ms”,这是 using 或 lock 语句的参数。 Dispose 调用或解锁将发生在本地的原始值上。

    请注意,此表单不会生成警告:

    // No warning
    MemoryStream ms;
    using (ms = new MemoryStream())
    {
        ms = null;
    }
    

    另一方面,除此之外,它们的行为方式确实相同。

    编辑:正如 bzlm 所指出的,变量在 using 语句之后仍然在范围内这一事实意味着它通常不是一个好主意。但是,已处置的对象并非总是不可用。例如:

    MemoryStream ms = new MemoryStream();
    using (ms) 
    {
        // Do stuff
    }
    byte[] data = ms.ToArray();
    

    这样就可以了——MemoryStream 会保留数据,即使数据已被处理。不过我还是觉得有点不对劲。

    【讨论】:

    • "您可以实例化资源对象,然后将变量传递给 using 语句,但这不是最佳实践。在这种情况下,即使控制离开 using 块,对象仍保留在范围内它可能无法再访问其非托管资源”似乎也很相关。
    • 谢谢乔恩。我以前从未见过如此深入的范围。
    【解决方案2】:

    是的,Dispose() 在这两个示例中都会被调用。它们在功能上是等效的,只是在第二个示例中,已处理的 StreamReader 仍然在范围内。因此首选第一种方法,因为使用已处置的对象通常是个坏主意。

    但是正如其他人指出的那样,有时可以使用已处置的对象。在这种情况下,您可能希望使用第二个示例。但是你必须知道你在做什么,如果可能的话我会避免它。

    【讨论】:

    • 首选第一种形式,以避免使用 sr2 污染封闭函数的范围,在 using 构造之后指向已处置的对象。
    【解决方案3】:

    两个代码几乎相同。一旦控制离开 using 块,就会调用 Dispose。如果在此之前发生异常,则在任何一种情况下都不会调用它。但除了异步异常之外,这不会在您的代码中发生。

    Is C#'s using statement abort-safe?

    是一个类似的讨论,关注与线程中止的交互。

    【讨论】:

      【解决方案4】:

      using 接受一个实现IDisposable 的对象。当编译器生成代码以在 using 块的末尾调用 Dispose 时,不会考虑该对象的创建方式和位置。

      【讨论】:

        【解决方案5】:

        好问题。我会说是的,因为using 代码块只不过是以下的语法糖:

        try
        {
           var myObj = <parameter from using>
           <using block code>
        }
        finally
        {
           myObj.Dispose();
        }
        

        请注意,当替换您的 using 块时,您最终处理的变量具有另一个在代码块之外可见的句柄 (sr2)。此引用将防止实例在 using 语句之后被垃圾收集,但由于它已被释放,除非它足够聪明地从中间范围的 Dispose() 中恢复,否则它不会有太大用处。

        【讨论】:

        • myObj 变量声明将在 try 块之外,否则它在 finally 块中不可见。
        【解决方案6】:

        MSDN 表示初始化期间的异常不完全相等。还要考虑以下范围场景:

        //Sample 1
        using (StreamReader sr1 = new StreamReader(@"C:\Data.txt"))
        {
            string s1 = sr1.ReadToEnd();
            //Do something with s1...
        }
        sr1.ReadToEnd() // sr1 not in scope.
        

        不编译,但是

        //Sample 2
        StreamReader sr2 = new StreamReader(@"C:\Data.txt");
        using (sr2)
        {
            string s2 = sr2.ReadToEnd();
            //Do something with s2...
        }
        
        sr2.ReadToEnd() // possible to write, but accessing a disposed object.
        

        编译但访问已处置的对象。

        【讨论】:

          猜你喜欢
          • 2013-06-30
          • 2011-09-12
          • 2017-10-29
          • 1970-01-01
          • 2017-12-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多