C#中“使用”的用途是什么?


319

用户kokos通过提及关键字回答了C#问题的精彩“ 隐藏功能”using。您能详细说明一下吗?有什么用using


这是支持RAII惯用语的C#方式:hackcraft.net/raii
Nemanja Trifunovic

1
您可以对已实现IDispose接口的对象使用。当该对象超出范围时,using将调用Dispose方法。即使发生任何异常,它也保证可以调用Dispose。它的工作方式类似于finally子句并执行Dispose。
CharithJ

Answers:


480

的原因 using语句是要确保对象在超出范围后立即被处置,并且不需要显式代码即可确保这种情况发生。

理解C#(codeproject)中的'using'语句使用实现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将被处置。


129
请注意,对象放置正确并不一定要解决问题,而是对象是否及时放置更重要。实现IDisposable的对象(该对象持有流和文件句柄之类的非托管资源)还将实现终结器,该终结器将确保在垃圾回收期间调用Dispose。问题在于GC可能不会在相当长的时间内发生。using确保Dispose在处理完对象后立即调用该方法。
约翰·桑德斯

1
请注意,当MyRessource是struct 时,生成的代码有些不同。很明显,没有针对无效性的测试,也没有对的装箱IDisposable。发出了受限的虚拟呼叫。
罗曼·威尔第

4
为什么没人提到using也用于导入名称空间?
凯尔·德莱尼

3
请注意,如果您直接编写第二版代码,则结果将不同。如果使用using,则内置在其中的变量为只读。如果没有该using语句,就无法对局部变量实现此目的。
Massimiliano Kraus

1
@JohnSaunders此外,不能保证可以调用终结器。
Pablo H

124

由于仍有很多人这样做:

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
}

2
是否可以在一个using语句中使用不同类型的多个对象?
Agnel Kurian

12
@AgnelKurian否:“错误CS1044:在for,using,fixed或clarification语句中不能使用多个类型”
David Sykes 2014年

10
这如何回答这个问题?
利亚姆

我实际上不知道我可以在单个代码块之前使用Statemen编写两个代码(每次都会嵌套它们)。
kub1x

97

像这样的事情:

using (var conn = new SqlConnection("connection string"))
{
   conn.Open();

    // Execute SQL statement here on the connection you created
}

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


1
如果我在方法内部使用“ using”怎么办,而我在using中间返回。有什么问题吗?
francisco_ssb

1
没有问题。在此处的示例中,即使您return位于using块中间,连接仍将关闭。
Joel Coehoorn

30

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

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

21

在某种意义上使用

using (var foo = new Bar())
{
  Baz();
}

实际上是try / finally块的简写。它等效于代码:

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

您当然会注意到,第一个代码段比第二个代码段要简洁得多,而且即使抛出异常,您也可能想做许多事情来进行清理。因此,我们提出了一个称为Scope的类,该类使您可以在Dispose方法中执行任意代码。因此,例如,如果您有一个名为IsWorking的属性,在尝试执行某个操作后始终希望将其设置为false,则可以这样做:

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

您可以在此处阅读有关我们的解决方案以及如何派生更多信息的信息


12

Microsoft文档指出,使用具有双重功能(https://msdn.microsoft.com/zh-cn/library/zhdeatwt.aspx),既可以用作指令也可以用作in 语句。作为声明,正如在其他答案中在此指出的那样,该关键字基本上是语法糖,用于确定处置IDisposable对象的范围。作为指令,它通常用于导入名称空间和类型。同样,作为指令,您可以创建约瑟夫(Joseph)和本·阿尔巴哈里(Ben Albahari)的别名(为名称空间和类型,如《 C#5.0简而言之:权威指南》(http://www.amazon.com/5-0-Nutshell-The-权威参考书/ 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#别名的很好的解释,也提到了利弊。


4
不会说谎:我讨厌将其using用作别名工具。当阅读代码时,这使我感到困惑-我已经知道System.Collections存在并且具有IEnumerable<T>该类。使用别名对其进行其他处理会使我感到困惑。我认为using FooCollection = IEnumerable<Foo>这是使以后的开发人员阅读代码并思考的一种方式,“到底是什么FooCollection,为什么在某个地方没有它的类?” 我从不使用它,并且会阻止它的使用。但这可能只是我。
阿里·罗斯

1
附录:我承认,有时可能会用到它,例如在您使用它定义委托的示例中。但是我认为这些是相对罕见的。
阿里·罗斯

10

过去,我已经使用很多它来处理输入和输出流。您可以很好地嵌套它们,这样可以消除通常遇到的许多潜在问题(通过自动调用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();
                }
            }
        }

8

只是添加一些令我惊讶的东西并没有出现。使用(我认为)最有趣的功能是,无论您如何退出using块,它都会始终处理该对象。这包括退货和例外。

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

引发异常还是返回列表都没有关系。DbContext对象将始终被处置。


6

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

Using frm as new Form1

Form1.ShowDialog

' do stuff here

End Using

1
您是说frm.ShowDialog吗?
UuDdLrLrSs

5

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

如果您使用非局部IDisposable变量,那么请始终实现IDisposablepattern

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


1):唯一的例外是–处理例外时。这样,Dispose在该finally块中显式调用的代码可能会更少。


5

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

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

如您所见,这被称为using别名指令,如果您想在代码中使其变得显而易见,则可以将其用于隐藏长引用,例如

LegacyEntities.Account

代替

CompanyFoo.CoreLib.x86.VBComponents.CompanyObjects.Account

或简单地

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

4

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

这使您可以使用非常好的using语法来表示所述操作的显式开始和结束。这也是System.Transactions东西的工作方式。


3

使用ADO.NET时,可以将键盘用于诸如连接对象或读取器对象之类的事情。这样,当代码块完成时,它将自动处理您的连接。


2
我只是补充说代码块甚至不必完成。即使在发生未处理的异常的情况下,using块也会处理资源。
harpo

只是为了进一步澄清,这是确保垃圾收集器需要当你把它想照顾你分配的,而不是在做这件事的方式希望。
moswald


3
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 
            }
    }

2

当您拥有要在使用后处置的资源时,使用using

例如,如果您分配了文件资源,而只需要在代码的一个部分中使用它来进行一点读写,则使用有助于在完成后立即处置文件资源。

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

例:

using (File file = new File (parameters))
{
    *code to do stuff with the file*
}

1

using关键字定义对象的作用域,然后在作用域完成后处置该对象。例如。

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

有关C#using关键字的MSDN文章,请参见此处


1

并不是说它非常重要,但是使用还可用于即时更改资源。是的,就像前面提到的那样,是一次性的,但是也许您在执行的其余部分中不希望它们与其他资源不匹配。因此,您希望对其进行处理,以免其他地方受到干扰。


1

感谢下面的评论,我将对此文章进行一些整理(我不应该在那时使用“垃圾收集”一词道歉):
当您使用using时,它将在对象上调用Dispose()方法在使用范围的末尾。因此,您的Dispose()方法中可以包含很多很棒的清理代码。
这里有一个要点,希望它可能不会引起关注:如果实现IDisposable,请确保在Dispose()实现中调用GC.SuppressFinalize(),否则自动垃圾收集将尝试并在某些情况下完成该过程要点,如果您已经对此进行了Dispose()d,至少会浪费资源。


它具有间接作用。因为您已明确处理该对象,所以它不需要终结处理,因此可以更早地进行GC处理。
肯特·布加亚特

1

将对象立即丢弃的合理使用的另一个示例:

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

1

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

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

1

using语句提供了一种方便的机制,可以正确使用IDisposable对象。通常,使用IDisposable对象时,应在using语句中声明并实例化它。using语句以正确的方式在对象上调用Dispose方法,并且(如前所述,当您使用它时)它还会导致对象本身在调用Dispose时就超出范围。在using块中,该对象是只读的,无法修改或重新分配。

来自:这里


1

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

为错误处理使用一个不同的名称会很好,也许更明显一些。


1

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

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

}

1

using语句告诉.NET一旦不再需要在using块中指定的对象释放。因此,对于需要清除之后的类,您应该使用“ using”块,例如System.IO Types。


1

usingC#中关键字的两种用法如下。

  1. 作为指示

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

    例:

    using System.IO;
  2. 作为声明

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

    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语句将自动调用conn.Close(),因为该using语句(using (SqlConnection conn = new SqlConnection(connString))与SqlDataReader对象的相同。并且,如果发生任何异常,它将自动关闭连接。

有关更多信息,请参见C#中的用法和重要性



-1

用作语句会自动在指定对象上调用处置。该对象必须实现IDisposable接口。只要它们是同一类型,就可以在一个语句中使用多个对象。

CLR将您的代码转换为MSIL。然后using语句将转换为try,并最终阻塞。这就是IL中using语句的表示方式。using语句转换为三个部分:获取,使用和处置。首先获取资源,然后将用法包含在带有finally子句的try语句中。然后,将对象放置在finally子句中。


-3

使用子句用于定义特定变量的范围。例如:

     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 becomes out of the defined scope.
            }

这可能会误导某人,使用来放置对象。也许您将其与代码块混淆了,如果您想限制变量的范围,可以为此使用嵌套代码块:public static void Main(params string [] args){{//嵌套代码块}}
luiseuardohd
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.