【问题标题】:What are the uses of "using" in C#?C#中“使用”的用途是什么?
【发布时间】:2010-09-09 16:15:53
【问题描述】:

用户kokos 通过提及using 关键字回答了精彩的Hidden Features of C# 问题。你能详细说明一下吗? using有什么用?

【问题讨论】:

  • 这是支持 RAII 习语的 C# 方式:hackcraft.net/raii
  • 您可以用于已实现 IDispose 接口的对象。当该对象超出范围时,使用将调用 Dispose 方法。即使发生任何异常,它也保证调用 Dispose。它像 finally 子句一样工作并执行 Dispose。

标签: c# using using-statement


【解决方案1】:

using关键字在C#中有两种用法,如下所示。

  1. 作为指令

    通常我们使用using 关键字在代码隐藏和类文件中添加命名空间。然后它使当前页面中的所有类、接口和抽象类及其方法和属性可用。

    例子:

    using System.IO;
    
  2. 作为声明

    这是在 C# 中使用 using 关键字的另一种方式。它在提高垃圾回收性能方面起着至关重要的作用。

    using 语句确保即使在创建对象和调用方法、属性等时发生异常,也会调用 Dispose()。 Dispose() 是 IDisposable 接口中存在的一种方法,可帮助实现自定义垃圾回收。换句话说,如果我正在执行一些数据库操作(插入、更新、删除)但不知何故发生了异常,那么这里 using 语句会自动关闭连接。无需显式调用连接 Close() 方法。

    另一个重要因素是它有助于连接池。 .NET 中的连接池有助于消除多次关闭数据库连接。它将连接对象发送到池以供将来使用(下一次数据库调用)。下次从应用程序调用数据库连接时,连接池会获取池中可用的对象。因此,它有助于提高应用程序的性能。所以当我们使用 using 语句时,控制器会自动将对象发送到连接池,不需要显式调用 Close() 和 Dispose() 方法。

    您可以通过使用 try-catch 块并显式调用 finally 块内的 Dispose() 来执行与 using 语句相同的操作。但是 using 语句会自动执行调用以使代码更简洁、更优雅。在 using 块中,对象是只读的,不能修改或重新分配。

    例子:

    string connString = "Data Source=localhost;Integrated Security=SSPI;Initial Catalog=Northwind;";
    
    using (SqlConnection conn = new SqlConnection(connString))
    {
          SqlCommand cmd = conn.CreateCommand();
          cmd.CommandText = "SELECT CustomerId, CompanyName FROM Customers";
          conn.Open();
          using (SqlDataReader dr = cmd.ExecuteReader())
          {
             while (dr.Read())
             Console.WriteLine("{0}\t{1}", dr.GetString(0), dr.GetString(1));
          }
    }
    

在前面的代码中,我没有关闭任何连接;它会自动关闭。由于 using 语句 (using (SqlConnection conn = new SqlConnection(connString)) 和 SqlDataReader 对象相同,using 语句将自动调用 conn.Close()。而且如果发生任何异常,它会自动关闭连接。

有关详细信息,请参阅 Usage and Importance of Using in C#

【讨论】:

    【解决方案2】:

    using as 语句会自动调用指定的 dispose 目的。该对象必须实现 IDisposable 接口。它是 可以在一个语句中使用多个对象,只要它们是 同类型。

    CLR 将您的代码转换为CILusing 语句被转换为 try 和 finally 块。这就是 using 语句在 CIL 中的表示方式。 using 语句被翻译成三个部分:获取、使用和处置。首先获取资源,然后将使用情况包含在带有 finally 子句的 try 语句中。然后对象在 finally 子句中被释放。

    【讨论】:

      【解决方案3】:

      using 语句告诉 .NET 在不再需要时释放 using 块中指定的对象。

      因此,您应该对需要清理的类使用“使用”块,例如 System.IO 类型。

      【讨论】:

        【解决方案4】:

        只是添加了一些令我惊讶的东西并没有出现。 using 最有趣的特性(在我看来)是,无论您如何退出 using 块,它总是会释放对象。这包括退货和例外。

        using (var db = new DbContext())
        {
            if(db.State == State.Closed)
                throw new Exception("Database connection is closed.");
            return db.Something.ToList();
        }
        

        抛出异常或返回列表都没有关系。 DbContext 对象将始终被释放。

        【讨论】:

          【解决方案5】:

          using 语句提供了一种方便的机制来正确使用 IDisposable 对象。通常,当您使用 IDisposable 对象时,您应该在 using 语句中声明和实例化它。

          using 语句以正确的方式调用对象的 Dispose 方法,并且(当您如前所示使用它时)它还会导致对象本身在 Dispose 后立即超出范围叫做。在 using 块中,对象是只读的,不能修改或重新分配。

          来自here

          【讨论】:

          • 链接并不是真正的链接(太不具体),而是一个通用的搜索页面(“654,563 个“使用”的结果)。
          【解决方案6】:

          using 子句用于定义特定变量的范围。

          例如:

          Using(SqlConnection conn = new SqlConnection(ConnectionString)
          {
              Conn.Open()
          
              // Execute SQL statements here.
              // You do not have to close the connection explicitly
              // here as "USING" will close the connection once the
              // object Conn goes out of the defined scope.
          }
          

          【讨论】:

          • 这可能会误导某人,使用 is 来处理对象。也许您将此与代码块混淆了,如果您想限制变量的范围,您可以使用嵌套代码块: public static void Main(params string[] args){ { //nested code block } }跨度>
          • @luise.. 对。 1. using 用于释放对象消耗的内存。
          【解决方案7】:
          public class ClassA:IDisposable
          {
              #region IDisposable Members
              public void Dispose()
              {
                  GC.SuppressFinalize(this);
              }
              #endregion
          }
          

          public void fn_Data()
          {
              using (ClassA ObjectName = new ClassA())
              {
                  // Use objectName
              }
          }
          

          【讨论】:

          • 解释一下。例如,想法/要点是什么?请通过editing (changing) your answer 回复,而不是在 cmets 中(without "Edit:"、"Update:" 或类似的 - 答案应该看起来像是今天写的)。
          【解决方案8】:

          使用,在

          的意义上
          using (var foo = new Bar())
          {
            Baz();
          }
          

          实际上是 try/finally 块的简写。相当于代码:

          var foo = new Bar();
          try
          {
            Baz();
          }
          finally
          {
            foo.Dispose();
          }
          

          当然,您会注意到,第一个 sn-p 比第二个更简洁,而且即使抛出异常,您也可能需要做很多事情作为清理。因此,我们提出了一个我们称为 Scope 的类,它允许您在 Dispose 方法中执行任意代码。因此,例如,如果您有一个名为 IsWorking 的属性,在尝试执行操作后您总是想将其设置为 false,您可以这样做:

          using (new Scope(() => IsWorking = false))
          {
            IsWorking = true;
            MundaneYetDangerousWork();
          }
          

          您可以阅读更多关于我们的解决方案以及我们如何获得它的信息here

          【讨论】:

            【解决方案9】:

            using 的另一个重要用途是在实例化模态对话框时。

            Using frm as new Form1
            
                Form1.ShowDialog
            
                ' Do stuff here
            
            End Using
            

            【讨论】:

            • 你是说frm.ShowDialog吗?
            • 问题是关于 C#。它在 VB.NET 中的工作方式是否相同,还是只是看起来那样?它是在哪个版本的 VB.NET(以及相应版本的 Visual Studio)中引入的?
            【解决方案10】:

            并不是说它非常重要,但 using 也可以用于动态更改资源。

            是的,如前所述,一次性的,但也许您特别不希望在其余执行期间它们与其他资源不匹配的资源。所以你想把它处理掉,这样它就不会干扰其他地方。

            【讨论】:

              【解决方案11】:

              using 用于当您拥有想要在使用后处置的资源。

              例如,如果你分配了一个 File 资源,并且只需要在一段代码中使用它来进行一点阅读或写作,那么 using 有助于在你完成后立即处理 File 资源。

              正在使用的资源需要实现 IDisposable 才能正常工作。

              例子:

              using (File file = new File (parameters))
              {
                  // Code to do stuff with the file
              }
              

              【讨论】:

                【解决方案12】:

                using 关键字定义对象的作用域,然后在作用域完成时释放对象。例如。

                using (Font font2 = new Font("Arial", 10.0f))
                {
                    // Use font2
                }
                

                有关 C# using 关键字的 MSDN 文章,请参阅 here

                【讨论】:

                • 链接已失效:"Visual Studio 2005 Retired documentation"
                【解决方案13】:

                “using”也可用于解决命名空间冲突。

                请参阅 http://www.davidarno.org/c-howtos/aliases-overcoming-name-conflicts/,了解我就该主题撰写的简短教程。

                【讨论】:

                • 链接已损坏:“嗯。我们无法找到该站点。我们无法连接到 www.davidarno.org 上的服务器。”跨度>
                【解决方案14】:

                using 可用于调用 IDisposable。它也可以用于给类型起别名。

                using (SqlConnection cnn = new SqlConnection()) { /* Code */}
                using f1 = System.Windows.Forms.Form;
                

                【讨论】:

                  【解决方案15】:

                  当你使用 using 时,它会在 using 作用域的末尾调用对象的 Dispose() 方法。所以你可以在你的 Dispose() 方法中有很多很棒的清理代码。

                  要点:

                  如果您实现 IDisposable,请确保在您的 Dispose() 实现中调用 GC.SuppressFinalize(),否则自动垃圾收集将尝试出现并在某些时候完成它,这至少会浪费如果你已经 Dispose()d 了资源。

                  【讨论】:

                  • 它有一个间接的影响。因为你已经明确地释放了对象,所以它不需要终结,因此可以更早地被 GC。
                  【解决方案16】:

                  using 语句的原因是为了确保对象一旦超出范围就被释放,并且不需要显式代码来确保发生这种情况。

                  就像在 Understanding the 'using' statement in C# (codeproject)Using objects that implement IDisposable (microsoft) 中一样,C# 编译器会转换

                  using (MyResource myRes = new MyResource())
                  {
                      myRes.DoSomething();
                  }
                  

                  { // Limits scope of myRes
                      MyResource myRes= new MyResource();
                      try
                      {
                          myRes.DoSomething();
                      }
                      finally
                      {
                          // Check for a null resource.
                          if (myRes != null)
                              // Call the object's Dispose method.
                              ((IDisposable)myRes).Dispose();
                      }
                  }
                  

                  C# 8 引入了一种新语法,名为“using declarations”:

                  using 声明是一个以 using 关键字开头的变量声明。它告诉编译器被声明的变量应该被放置在封闭范围的末尾。

                  所以上面的等效代码是:

                  using var myRes = new MyResource();
                  myRes.DoSomething();
                  

                  当控件离开包含范围(通常是方法,但也可以是代码块)时,myRes 将被释放。

                  【讨论】:

                  • 请注意,这不一定是对象被正确处置的问题,而更多的是是否及时处置。实现 IDisposable 的对象持有非托管资源(如流和文件句柄)也将实现一个终结器,以确保在垃圾回收期间调用 Dispose。问题是 GC 可能不会在相对较长的时间内发生。 using 确保在您完成对象后调用 Dispose
                  • 请注意,当MyRessource 是结构时,生成的代码会有些不同。显然没有测试无效性,也没有拳击到IDisposable。发出受约束的虚拟调用。
                  • 为什么没有人提到 using 也用于导入命名空间?
                  • 注意,如果直接写第二版的代码,结果是不一样的。如果您使用using,则其中内置的变量是只读的。如果没有 using 语句,就无法为局部变量实现这一点。
                  • @JohnSaunders 此外,不保证会调用终结器。
                  【解决方案17】:

                  Rhino Mocks Record-playback Syntax 有趣地使用了using

                  【讨论】:

                  • 它与调用 Playback 和验证所有内容基本相同,它只是在 Dispose() 方法中执行此操作
                  【解决方案18】:

                  这样的事情:

                  using (var conn = new SqlConnection("connection string"))
                  {
                     conn.Open();
                  
                      // Execute SQL statement here on the connection you created
                  }
                  

                  这个SqlConnection 将被关闭而无需显式调用.Close() 函数,并且这种情况会发生即使抛出异常,也不需要try/@987654325 @/finally.

                  【讨论】:

                  • 如果我在方法中使用“使用”,然后在使用过程中返回怎么办。有什么问题吗?
                  • 没有问题。在此处的示例中,即使您从using 块的中间return,连接仍将关闭。
                  【解决方案19】:

                  因为很多人仍然这样做:

                  using (System.IO.StreamReader r = new System.IO.StreamReader(""))
                  using (System.IO.StreamReader r2 = new System.IO.StreamReader("")) {
                     //code
                  }
                  

                  我猜很多人还不知道你能做到:

                  using (System.IO.StreamReader r = new System.IO.StreamReader(""), r2 = new System.IO.StreamReader("")) {
                     //code
                  }
                  

                  【讨论】:

                  • 是否可以在一个using语句中使用多个不同类型的对象?
                  • @AgnelKurian 否:“错误 CS1044:在 for、using、fixed 或声明语句中不能使用多个类型”
                  • 这如何回答这个问题?
                  • 我实际上不知道我可以在单个代码块之前编写两个 using 语句(每次都会嵌套它们)。
                  【解决方案20】:

                  它也可以用于创建示例范围:

                  class LoggerScope:IDisposable {
                     static ThreadLocal<LoggerScope> threadScope = 
                          new ThreadLocal<LoggerScope>();
                     private LoggerScope previous;
                  
                     public static LoggerScope Current=> threadScope.Value;
                  
                     public bool WithTime{get;}
                  
                     public LoggerScope(bool withTime){
                         previous = threadScope.Value;
                         threadScope.Value = this;
                         WithTime=withTime;
                     }
                  
                     public void Dispose(){
                         threadScope.Value = previous;
                     }
                  }
                  
                  
                  class Program {
                     public static void Main(params string[] args){
                         new Program().Run();
                     }
                  
                     public void Run(){
                        log("something happend!");
                        using(new LoggerScope(false)){
                            log("the quick brown fox jumps over the lazy dog!");
                            using(new LoggerScope(true)){
                                log("nested scope!");
                            }
                        }
                     }
                  
                     void log(string message){
                        if(LoggerScope.Current!=null){
                            Console.WriteLine(message);
                            if(LoggerScope.Current.WithTime){
                               Console.WriteLine(DateTime.Now);
                            }
                        }
                     }
                  
                  }
                  

                  【讨论】:

                    【解决方案21】:

                    Microsoft 文档指出 using 具有双重功能 (https://msdn.microsoft.com/en-us/library/zhdeatwt.aspx),既可以作为 指令 也可以在 语句 中使用。作为声明,正如在其他答案中指出的那样,关键字基本上是语法糖,用于确定处置 IDisposable 对象的范围。作为一个指令,它通常用于导入命名空间和类型。同样作为指令,您可以为命名空间和类型创建 别名,正如 Joseph 和 Ben Albahari 所著的“C# 5.0 In a Nutshell: The Definitive Guide”(http://www.amazon.com/5-0-Nutshell-The-Definitive-Reference-ebook/dp/B008E6I1K8) 一书中所指出的那样。一个例子:

                    namespace HelloWorld
                    {
                        using AppFunc = Func<IDictionary<DateTime, string>, List<string>>;
                        public class Startup
                        {
                            public static AppFunc OrderEvents() 
                            {
                                AppFunc appFunc = (IDictionary<DateTime, string> events) =>
                                {
                                    if ((events != null) && (events.Count > 0))
                                    {
                                        List<string> result = events.OrderBy(ev => ev.Key)
                                            .Select(ev => ev.Value)
                                            .ToList();
                                        return result;
                                    }
                                    throw new ArgumentException("Event dictionary is null or empty.");
                                };
                                return appFunc;
                            }
                        }
                    }
                    

                    这是明智的做法,因为滥用这种做法会损害代码的清晰度。 DotNetPearls (http://www.dotnetperls.com/using-alias) 中对 C# 别名有很好的解释,也提到了优缺点。

                    【讨论】:

                    • 不会撒谎:我讨厌使用using 作为别名工具。阅读代码时让我感到困惑——我已经知道 System.Collections 存在并且有 IEnumerable&lt;T&gt; 类。使用别名来称呼它会使我感到困惑。我认为using FooCollection = IEnumerable&lt;Foo&gt; 是一种让后来的开发人员阅读代码并思考的方式,“FooCollection 到底是什么,为什么在某个地方没有一个类?”我从不使用它,并且会阻止它的使用。但这可能只是我。
                    • 附录:我承认它偶尔可能会有用处,就像在您使用它来定义委托的示例中一样。但我认为这些是相对罕见的。
                    【解决方案22】:

                    对我来说,“使用”这个名称有点令人困惑,因为它可以是导入命名空间的指令或用于错误处理的语句(如此处讨论的语句)。

                    错误处理的另一个名称会很好,而且可能更明显。

                    【讨论】:

                      【解决方案23】:

                      您可以通过以下示例使用别名命名空间:

                      using LegacyEntities = CompanyFoo.CoreLib.x86.VBComponents.CompanyObjects;
                      

                      如您所见,这称为 using alias 指令,如果您想在代码中明确指出您所指的内容,它可用于隐藏冗长的引用 例如

                      LegacyEntities.Account
                      

                      而不是

                      CompanyFoo.CoreLib.x86.VBComponents.CompanyObjects.Account
                      

                      或者干脆

                      Account   // It is not obvious this is a legacy entity
                      

                      【讨论】:

                        【解决方案24】:

                        大括号之外的所有东西都被处理掉了,所以如果你不使用它们,最好处理掉你的对象。之所以如此,是因为如果您有一个 SqlDataAdapter 对象,并且在应用程序生命周期中仅使用一次,并且您只填充了一个数据集并且不再需要它,则可以使用以下代码:

                        using(SqlDataAdapter adapter_object = new SqlDataAdapter(sql_command_parameter))
                        {
                           // do stuff
                        } // here adapter_object is disposed automatically
                        

                        【讨论】:

                          【解决方案25】:

                          立即处置对象的另一个合理使用示例:

                          using (IDataReader myReader = DataFunctions.ExecuteReader(CommandType.Text, sql.ToString(), dp.Parameters, myConnectionString)) 
                          {
                              while (myReader.Read()) 
                              {
                                  MyObject theObject = new MyObject();
                                  theObject.PublicProperty = myReader.GetString(0);
                                  myCollection.Add(theObject);
                              }
                          }
                          

                          【讨论】:

                            【解决方案26】:

                            总之,当您使用实现IDisposable 的类型的局部变量时,总是,无一例外地使用using1

                            如果你使用非本地的IDisposable 变量,那么总是实现IDisposable pattern

                            两个简单的规则,没有例外1。否则,防止资源泄漏是 *ss 的真正痛苦。


                            1):唯一的例外是 – 当您处理异常时。在finally 块中显式调用Dispose 的代码可能会更少。

                            【讨论】:

                              【解决方案27】:

                              我过去经常使用它来处理输入和输出流。您可以很好地嵌套它们,它消除了您通常遇到的许多潜在问题(通过自动调用 dispose)。例如:

                                      using (FileStream fs = new FileStream("c:\file.txt", FileMode.Open))
                                      {
                                          using (BufferedStream bs = new BufferedStream(fs))
                                          {
                                              using (System.IO.StreamReader sr = new StreamReader(bs))
                                              {
                                                  string output = sr.ReadToEnd();
                                              }
                                          }
                                      }
                              

                              【讨论】:

                                【解决方案28】:

                                有趣的是,您还可以将 using/IDisposable 模式用于其他有趣的事情(例如 Rhino Mocks 使用它的另一点)。基本上,您可以利用编译器总是在“已使用”对象上调用 .Dispose 的事实。如果您在某个操作之后需要发生某些事情……具有明确的开始和结束的事情……那么您可以简单地创建一个 IDisposable 类,该类在构造函数中开始操作,然后在 Dispose 方法中完成。

                                这允许您使用非常好的 using 语法来表示所述操作的显式开始和结束。这也是 System.Transactions 的工作原理。

                                【讨论】:

                                  【解决方案29】:

                                  使用 ADO.NET 时,您可以将键用于连接对象或读取器对象等内容。这样,当代码块完成时,它会自动释放您的连接。

                                  【讨论】:

                                  • 我只想补充一点,代码块甚至不必完成。即使发生未处理的异常,using 块也会释放资源。
                                  • 为了进一步澄清,这是一种确保垃圾收集器在您需要时处理您的分配的方法,而不是在 想要时进行处理。
                                  猜你喜欢
                                  • 1970-01-01
                                  • 2015-06-24
                                  • 2013-11-03
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 2014-01-15
                                  • 2011-08-07
                                  • 2012-06-03
                                  相关资源
                                  最近更新 更多