没有花括号的using语句的作用域是什么?
本文关键字:语句 作用域 是什么 using | 更新日期: 2023-09-27 18:13:23
我继承了以下代码:
using (var dataAccessConnection = da.GetConnection()) //no opening curly brace here
using (var command = new SqlCommand(sql, dataAccessConnection.Connection))
{
command.CommandType = CommandType.Text;
using (var sqlDataReader = command.ExecuteReader(CommandBehavior.CloseConnection))
{
while (sqlDataReader.Read())
{
rueckgabe.Add(new Myclass
{
Uid = Guid.NewGuid(),
ImportVersionUid = versionUid,
MyProperty = Convert.ToInt32(sqlDataReader["MyProperty"])
});
}
}
command.Connection.Close();
dataAccessConnection.Connection.Close();
}
查看代码,我期望在using子句后面有一个左花括号。
代码编译并执行预期的操作。应用程序的行为不可预测。有时它不能访问数据库服务器。
这段代码有意义吗?dataAccessConnection是否具有正确的作用域?
从c# 8.0开始,using
关键字可以在一次性对象的变量声明中作为属性使用(参考)。语义与您所期望的一样——对象在作用域结束时被自动处置。
public class Disposable : IDisposable
{
string name;
public Disposable(string name)
{
this.name = name;
}
public void Dispose()
{
Console.WriteLine(name + " disposed");
}
public void Identify()
{
Console.WriteLine(name);
}
static void Main(string[] args)
{
using Disposable d1 = new Disposable("Using 1");
Disposable d2 = new Disposable("No Using 2");
using Disposable d3 = new Disposable("Using 3");
Disposable d4 = new Disposable("No Using 4");
d1.Identify();
d2.Identify();
d3.Identify();
d4.Identify();
}
}
<>之前使用1不使用2使用3不使用4使用3处置使用1处置
没有显式花括号的using
语句只适用于以下语句:
using (Idisp1)
// use it
// it's disposed
因此,当链接时,它们以相同的方式工作。这里的第二个using
充当单个语句。
using (Idisp1)
using (Idisp2)
{
}
评论者stakx建议格式化,以明确编译器如何读取using块。实际上,这些通常被格式化为遇到的OP:
using (Idisp1)
using (Idisp2)
{
}
等于:
using (Idisp1)
{
using (Idisp2)
{
}
}
注意,顶部的第一个总是最后一个被处理的。因此,在前面的所有示例中,Idisp2.Dispose()
在Idisp1.Dispose()
之前被调用。这在很多情况下是不相关的,你会做这样的事情,但我相信你应该总是知道你的代码会做什么,并做出明智的决定不关心。
阅读网页的一个例子是:
HttpWebRequest req = ...;
using (var resp = req.GetResponse())
using (var stream = resp.GetResponseStream())
using (var reader = new StreamReader(stream))
{
TextBox1.Text = reader.ReadToEnd(); // or whatever
}
获取响应、获取流、获取读取器、读取流、释放读取器、释放流,最后释放响应。
注意,正如评论者Nikhil Agrawal指出的那样,这是一个关于块的语言特性,它不是特定于using
关键字的。例如,同样适用于if
块:
if (condition)
// may or may not execute
// definitely will execute
和
if (condition1)
if (condition2)
// will execute if both are true
// definitely will execute
虽然你当然不应该这样使用if
语句,因为它读起来很可怕,但我认为它可以帮助你理解using
的情况。我个人对链接using
块非常满意。
c#语言规范(Version 5)将using
语句描述为:
使用叙述:
using (
resource-acquisition)
embedded-statement
:
using
语句获取一个或多个资源,执行语句,然后释放资源。
(我强调)
那么,我们最终是如何使用它和花括号呢?因为嵌入式语句的定义是:
embedded-statement:
块
empty-statement
表达式语句
selection-statement
迭代语句
跳转语句
try语句
checked-statement
unchecked-statement
lock语句
使用叙述
yield语句
:
最后,我们发现块被定义为:嵌入式语句非终结符用于出现在其他语句
中的语句
块允许在只允许一条语句的上下文中写入多条语句。
块:
{
statement-listopt}
所以基本上,花括号总是可以用于接受单个语句,而不是多个语句的情况。
碰巧的是,几乎总是,我们确实想要使用多个语句,所以花括号往往被视为if
, using
等语句的一部分。然而,事实上,它们是语言的一个独立部分。
现有的回答分别不正确或不完整。
实际上有两种情况:
-
using (var resp = req.GetResponse())
,它有下一条语句的作用域。
但也有
-
using var resp = req.GetResponse()
(注意使用compare to 1后缺少括号)
具有块作用域,例如当前块或方法。
因此,如果你在方法的根块中使用using var resp = req.GetResponse()
,那么using变量将具有方法作用域,实际上类似于go中的defer语句。
注意GO中的defer具有FUNCTION作用域,而c#中的using(不带括号)具有BLOCK作用域。
例子public static ILoggerFactory TestLogging()
{
// dotnet add package Microsoft.Extensions.Logging
// dotnet add package OpenTelemetry.Exporter.Console
using ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddOpenTelemetry(options =>
{
options.AddConsoleExporter();
});
});
ILogger<Program> logger = loggerFactory.CreateLogger<Program>();
System.GC.Collect();
var fac = loggerFactory.CreateLogger<Program>();
System.Console.WriteLine(fac);
logger.LogInformation(eventId: 123, "Hello from {name} {price}.", "tomato", 2.99);
if (logger.IsEnabled(LogLevel.Debug))
{
// If logger.IsEnabled returned false, the code doesn't have to spend time evaluating the arguments.
// This can be especially helpful if the arguments are expensive to calculate.
logger.LogDebug(eventId: 501, "System.Environment.Version: {version}.", System.Environment.Version);
}
return loggerFactory;
}
你可以看到在函数内部,你可以在使用loggerFactory调用GC.Collect()之后创建第二个记录器。
但是当您从函数返回loggerFactory时,请调用GC。收集并想要创建另一个记录器,它将抛出System.ObjectDisposedException。
var x = TestLogging();
System.GC.Collect();
var log = x.CreateLogger<Program>(); // throws ObjectDisposedException