用户kokos通过提及关键字回答了精彩的 C# 隐藏功能问题。using
你能详细说明一下吗?有什么用途using
?
29 回答
该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
将被释放。
由于很多人仍然这样做:
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 (var conn = new SqlConnection("connection string"))
{
conn.Open();
// Execute SQL statement here on the connection you created
}
这SqlConnection
将在不需要显式调用函数的情况下关闭,即使抛出异常.Close()
也会发生这种情况,不需要// 。try
catch
finally
using可用于调用 IDisposable。它也可以用于给类型起别名。
using (SqlConnection cnn = new SqlConnection()) { /* Code */}
using f1 = System.Windows.Forms.Form;
使用_
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();
}
您可以在此处阅读更多关于我们的解决方案以及我们如何获得它的信息。
Microsoft 文档指出using具有双重功能 ( https://msdn.microsoft.com/en-us/library/zhdeatwt.aspx ),既可以作为指令,也可以作为语句。作为一个声明,正如在其他答案中指出的那样,关键字基本上是语法糖,用于确定处置IDisposable对象的范围。作为指令,它通常用于导入命名空间和类型。此外,作为指令,您可以为命名空间和类型创建别名,正如“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# 别名的很好的解释,也提到了优缺点。
我过去经常使用它来处理输入和输出流。您可以很好地嵌套它们,它消除了您通常遇到的许多潜在问题(通过自动调用 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();
}
}
}
只是添加了一些令我惊讶的东西并没有出现。using最有趣的特性(在我看来)是,无论你如何退出using块,它总是会释放对象。这包括退货和例外。
using (var db = new DbContext())
{
if(db.State == State.Closed)
throw new Exception("Database connection is closed.");
return db.Something.ToList();
}
抛出异常或返回列表都没有关系。DbContext 对象将始终被释放。
using的另一个重要用途是在实例化模态对话框时。
Using frm as new Form1
Form1.ShowDialog
' Do stuff here
End Using
您可以通过以下示例使用别名命名空间:
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
有趣的是,您还可以将 using/IDisposable 模式用于其他有趣的事情(例如 Rhino Mocks 使用它的另一点)。基本上,您可以利用编译器总是在“使用”对象上调用 .Dispose 的事实。如果您在某个操作之后需要发生某些事情......有明确的开始和结束......那么您可以简单地创建一个 IDisposable 类,该类在构造函数中开始操作,然后在 Dispose 方法中完成。
这允许您使用非常好的 using 语法来表示所述操作的显式开始和结束。这也是 System.Transactions 的工作方式。
总之,当您使用实现 的类型的局部变量时IDisposable
,总是无一例外地使用using
1。
如果您使用非局部IDisposable
变量,则始终实现该IDisposable
模式。
两个简单的规则,没有例外1。否则,防止资源泄漏是 *ss 的真正痛苦。
1):唯一的例外是 – 当您处理异常时。然后在块Dispose
中显式调用的代码可能会更少。finally
使用 ADO.NET 时,您可以将密钥用于连接对象或读取器对象等内容。这样,当代码块完成时,它将自动处理您的连接。
"using" 也可用于解决命名空间冲突。
请参阅http://www.davidarno.org/c-howtos/aliases-overcoming-name-conflicts/了解我写的关于该主题的简短教程。
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
}
}
C#中关键字的两种用法using
如下。
作为指令
通常我们使用
using
关键字在代码隐藏和类文件中添加命名空间。然后它使当前页面中的所有类、接口和抽象类及其方法和属性可用。例子:
using System.IO;
作为声明
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() ,对于 SqlDataReader 对象也是如此。如果发生任何异常,它也会自动关闭连接。using
using (SqlConnection conn = new SqlConnection(connString)
有关详细信息,请参阅在 C# 中使用的用法和重要性。
using用于当你有一个资源在使用后你想要处理它。
例如,如果您分配了一个 File 资源,并且只需要在一段代码中使用它来进行少量读取或写入,那么 using 有助于在您完成后立即处理 File 资源。
正在使用的资源需要实现 IDisposable 才能正常工作。
例子:
using (File file = new File (parameters))
{
// Code to do stuff with the file
}
当你使用using时,它会在 using 作用域的末尾调用对象的 Dispose() 方法。所以你可以在你的 Dispose() 方法中有很多很棒的清理代码。
一个要点:
如果您实现 IDisposable,请确保在您的 Dispose() 实现中调用 GC.SuppressFinalize(),否则自动垃圾收集将尝试出现并在某个时候完成它,如果您这样做至少会浪费资源'已经 Dispose()d 了。
using关键字定义对象的范围,然后在范围完成时释放对象。例如。
using (Font font2 = new Font("Arial", 10.0f))
{
// Use font2
}
有关 C# using关键字的 MSDN 文章,请参见此处。
并不是说它非常重要,但using也可用于动态更改资源。
是的,如前所述是一次性的,但也许特别是您不希望在其余执行期间它们与其他资源不匹配的资源。所以你想处理它,这样它就不会干扰其他地方。
立即处置对象的另一个合理使用示例:
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);
}
}
大括号之外的所有东西都被处理掉了,所以如果你不使用它们,最好处理掉你的对象。之所以如此,是因为如果您有一个 SqlDataAdapter 对象并且您在应用程序生命周期中只使用它一次,并且您只填充一个数据集并且您不再需要它,您可以使用以下代码:
using(SqlDataAdapter adapter_object = new SqlDataAdapter(sql_command_parameter))
{
// do stuff
} // here adapter_object is disposed automatically
using语句提供了一种方便的机制来正确使用 IDisposable 对象。通常,当您使用 IDisposable 对象时,您应该在 using 语句中声明和实例化它。
using语句以正确的方式调用对象的Dispose 方法,并且(当您如前所示使用它时)它还会导致对象本身在调用 Dispose 时立即超出范围。在using块中,对象是只读的,不能修改或重新分配。
这来自这里。
对我来说,“使用”这个名称有点令人困惑,因为它可以是导入命名空间的指令或用于错误处理的语句(如这里讨论的语句)。
错误处理的不同名称会很好,而且可能更明显。
它还可以用于创建范围,例如:
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);
}
}
}
}
using语句告诉 .NET 在不再需要时释放 using 块中指定的对象。
因此,您应该将“使用”块用于需要在它们之后清理的类,例如System.IO类型。
Rhino Mocks Record-playback Syntax对using
.
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.
}