什么时候C#“使用”语句最有用?

时间:2009-04-15 16:35:06

标签: c# using-statement

因此,当退出using块时,using语句会自动调用正在“使用”的对象上的dispose方法吗?

但这何时有必要/有益吗?

例如,假设你有这种方法:

public void DoSomething()
{
    using (Font font1 = new Font("Arial", 10.0f))
    {
        // Draw some text here
    }
}

是否有必要在此处使用using语句,因为该对象是在方法中创建的?当方法退出时,不会丢弃Font对象吗?

或者在方法退出后,Dispose方法是否会在另一个时间运行?

例如,如果方法是这样的:

public void DoSomething()
{
    Font font1 = new Font("Arial", 10.0f);

    // Draw some text here
}

// Is everything disposed or cleared after the method has finished running?

14 个答案:

答案 0 :(得分:19)

使用非托管对象(如数据库连接)时,'using'语句最有用。

通过这种方式,无论代码块中发生什么,连接都会被关闭和处理。

有关更多讨论,请参阅CodeProject上的这篇文章: http://www.codeproject.com/KB/cs/tinguusingstatement.aspx

答案 1 :(得分:13)

如果没有using(或手动调用Dispose()),对象最终将被处理掉,而不是在确定的时间。也就是说,它可以在两天内立即发生,或者(在某些情况下)永远不会发生。

对于像网络连接这样的东西,你希望连接完成时关闭,而不是“随时”关闭,否则它会在占用套接字时闲置。

此外,对于像互斥锁这样的东西,你不希望“随时”释放它们,否则可能会导致死锁。

答案 2 :(得分:11)

此:

public void DoSomething()
{
    using (Font font1 = new Font("Arial", 10.0f))
    {
        // Draw some text here
    }
}

直接映射到:

public void DoSomething()
{
    {
        Font font1;
        try
        {
            font1 = new Font("Arial", 10.0f);
            // Draw some text here
        }
        finally
        {
            IDisposable disp = font1 as IDisposable;
            if (disp != null) disp.Dispose();
        }
    }
}

注意finally块:即使发生异常,也会处理对象。还要注意额外的匿名范围块:它意味着不仅要处理对象,而且它也超出了范围。

另一个重要的事情是保证立即处置。这是确定性。如果没有using语句或类似的构造,该对象在方法结束时仍会超出范围,最终可能会被收集。理想情况下,资源将被销毁,以便系统可以回收它。但“最终”可能不会发生一段时间,“理想情况下”和“意志”是非常不同的事情。

因此,“最终”并不总是足够好。像数据库连接,套接字,信号量/互斥量和(在这种情况下)GDI资源这样的资源经常受到严格限制,需要清理马上起来使用声明将确保发生这种情况。

答案 3 :(得分:9)

using构造强制执行 deterministic 处理 - 即释放资源。在上面的示例中,是的,如果您不使用“using”语句,则将处置该对象,但仅限于推荐的一次性模式(即,从类终结器中处理资源) (如果适用)已针对相关类实现(在您的示例中为Font类)。应该注意,using构造只能用于实现IDisposable接口的对象。在对象上存在该接口,允许使用“调用”Dispose方法。

此外,只有当垃圾收集器决定收集超出范围的Font对象时,才会释放底层资源。 .Net编程中的一个关键概念(以及大多数带有垃圾收集器的语言)只是因为一个对象超出范围并不意味着它被最终确定/释放/销毁等等。相反,垃圾收集器将执行清理 - 它确定的时间 - 当对象超出范围时不立即。

最后,using语句“烘焙”一个try / finally结构,以确保调用Dispose而不管包含的代码是否抛出异常。

答案 4 :(得分:2)

当需要处理资源并且它实现IDisposable接口时,“使用”发挥作用。

答案 5 :(得分:2)

这是使用真正的用途(基于你的例子)

Font font1 = new Font(...);
try
{
    // code that uses font...
}
finally
{
    if (font1 != null)
        font1.Dispose();
}

因此,您无需担心异常会使您的变量无法处理。

答案 6 :(得分:2)

  

当方法退出时,不会丢弃Font对象吗?

不,它将变为未引用,因此有资格进行垃圾回收。除非其他内容(例如,在另一个数据结构中保存的引用)保留在引用上。

  

或者在方法退出后,Dispose方法是否会在另一个时间运行?

是的,在一个过程中没有分配很多内存,这可能是该方法退出后相当长的一段时间。

垃圾收集本质上是异步和懒惰的,因此如果内存不是太有限,它就是确保释放内存的好方法;但几乎所有其他资源都很差。

答案 7 :(得分:2)

对我来说似乎很简单。

如果一个类实现了IDisposable,那么一旦你完成它,它几乎要求你就可以在你创建的任何实例上调用Dispose。这是我建议应该使用的模式,只要创建了这样的实例:

using (var instanceName = new DisposableClass())
{
    // Your code here
}

它很简单,它很干净,除了WCF代理类之外,它还可以运行。对于那些,请参阅http://www.iserviceoriented.com/blog/post/Indisposable+-+WCF+Gotcha+1.aspx

答案 8 :(得分:0)

当您希望确定性地在对象上调用清理代码而不管异常时(因为using语句实际上只是try/finally),它们在任何情况下都很有用。

大多数情况下,它们用于清理非托管资源,但您可以根据自己的需要使用它们。

答案 9 :(得分:0)

在try-catch-finally块和调用Dispose方法之上纯粹是语法糖。它甚至不一定定义词法范围,因为您可以将其设为实例变量。基本上,它使您的代码更清晰,更易于维护。

答案 10 :(得分:0)

Dispose在退出using语句时被调用,因为它被该构造显式调用。当变量超出范围(退出方法)时,不会显式调用dispose方法。

你可能观察到的行为看起来就是通常实现IDisposable的东西也会在类析构函数中调用Dispose方法,并且在变量超出范围后很快就会调用析构函数COULD,但不能保证。析构函数由垃圾收集器调用。

答案 11 :(得分:0)

我喜欢这样使用它们:

    static public void AddSampleData(String name)
    {
        String CreateScript = GetScript(String.Format("SampleData_{0}", name));
        using (IDbConnection MyConnection = GetConnection())
        {
            MyConnection.Open();
            IDbCommand MyCommand = MyConnection.CreateCommand();
            foreach (String SqlScriptLine in CreateScript.Split(';'))
            {
                String CleanedString = SqlScriptLine.Replace(";", "").Trim();
                if (CleanedString.Length == 0)
                    continue;

                MyCommand.CommandText = CleanedString;
                int Result = MyCommand.ExecuteNonQuery();
            }
        }
    }

它们非常适用于您不希望错误检查代码仅清理并将错误传递下来的情况。替代方法如下所示:

    static public void AddSampleData(String name)
    {
        String CreateScript = GetScript(String.Format("SampleData_{0}", name));

        IDbConnection MyConnection = null;
        try
        {
            IDbConnection MyConnection = GetConnection();
            MyConnection.Open();
            IDbCommand MyCommand = MyConnection.CreateCommand();
            foreach (String SqlScriptLine in CreateScript.Split(';'))
            {
                String CleanedString = SqlScriptLine.Replace(";", "").Trim();
                if (CleanedString.Length == 0)
                    continue;

                MyCommand.CommandText = CleanedString;
                int Result = MyCommand.ExecuteNonQuery();
            }
        }
        finally
        {
            if (MyConnection != null
                && MyConnection.State == ConnectionState.Open)
                MyConnection.Close();
        }
    }

坦率地说,哪种方法更容易阅读?形式相同:

    public void ChangeConfig()
    {
        using (ConfigForm MyForm = new ConfigForm())
        {
            DialogResult ConfigResult = MyForm.ShowDialog();
            if (ConfigResult == DialogResult.OK)
                SaveConfig();
        }

        ConfigForm MyForm = new ConfigForm();
        DialogResult ConfigResult = MyForm.ShowDialog();
        MyForm.Dispose();
        if (ConfigResult == DialogResult.OK)
            SaveConfig();
    }

答案 12 :(得分:0)

关于确定性与不确定性以及using如何在幕后工作的所有答案都是正确的。

但是要选择您的特定示例,请不要忘记,几乎所有 System.Drawing(GDI +包装器)对象也包含对非托管内存的引用,因此在广泛使用时可能会遇到麻烦没有正确处理它们(using是最简单的方法)。

答案 13 :(得分:0)

我在这里问了一个非常相似的问题:

Resources that have to be manually cleaned up in C#?

我得到了以下形式的一条很好的建议:

  

一般情况下,如果某个东西有Dispose方法,你应该在完成后调用它,或者,如果可以的话,将它包装在using语句中。