C# 8 使用声明范围混淆
C# 8 Using Declaration Scope Confusion
使用新的 C# 8 Using 声明语法,第二个连续 using 语句的包含范围是什么?
TL;DR
在 C# 8 之前,有一个连续的 using 语句,如:
using(var disposable = new MemoryStream())
{
using(var secondDisposable = new StreamWriter(disposable))
{}
}
将扩展为如下内容 (My Source):
MemoryStream disposable = new MemoryStream();
try {
{
StreamWriter secondDisposable = new StreamWriter(disposable);
try{
{}
}
finally {
if(secondDisposable != null) ((IDisposable)secondDisposable).Dispose();
}
}
}
finally {
if(disposable != null) ((IDisposable)disposable).Dispose();
}
我知道还有另外两种可能的扩展,但它们大致都是这样
升级到 C# 8 后,Visual studio 提供了一个代码清理建议,我不确定我认为它是等效的建议。
将上面连续的using语句转换为:
using var disposable = new MemoryStream();
using var secondDisposable = new StreamWriter(disposable);
对我来说,这会将第二个的范围更改为与第一个相同的范围。在这种情况下,它可能会巧合地以正确的顺序处理流,但我不确定我是否喜欢依赖这种巧合。
为了清楚 VS 要求我做什么:我首先转换了内部(这是有道理的,因为内部仍然包含在外部的范围内)。然后我转换了外部(这在本地有意义,因为它仍然包含在方法的范围内)。这两个清理的组合是我很好奇的。
我也认识到我对此的想法可能略有(甚至是显着)偏差,但根据我今天的理解,这似乎不正确。我的评估中缺少什么?我错了吗?
我唯一能想到的是,在声明语句之后的所有内容的扩展中插入了某种隐式作用域。
In this case, It would probably coincidentally dispose of the streams in the correct order, but I'm not certain I like to rely on that happy coincidence.
The using
locals will then be disposed in the reverse order in which they are declared.
所以,是的,他们已经考虑过了,并按照预期的顺序进行处理,就像之前的链接 using 语句一样。
我想看看使用它的真实功能。编译器不会随意更改分配或处置的范围或顺序。如果你有这样的方法:
void foo()
{
using(var ms = new MemoryStream())
{
using(var ms2 = new MemoryStream())
{
/// do something
}
}
}
然后 Dispose()
顺序无关紧要,因此编译器可以安全地安排它认为合适的事情。在其他情况下,顺序 是 很重要,编译器应该足够聪明以识别这一点。我不会在 "coincidence" 下提交 "good AST analysis."
说明Daminen的答案;当你有类似的方法时;
public void M()
{
using var f1 = new System.IO.MemoryStream(null,true);
using var f2 = new System.IO.MemoryStream(null,true);
using var f3 = new System.IO.MemoryStream(null,true);
}
IL将其转换为;
public void M()
{
MemoryStream memoryStream = new MemoryStream(null, true);
try
{
MemoryStream memoryStream2 = new MemoryStream(null, true);
try
{
MemoryStream memoryStream3 = new MemoryStream(null, true);
try
{
}
finally
{
if (memoryStream3 != null)
{
((IDisposable)memoryStream3).Dispose();
}
}
}
finally
{
if (memoryStream2 != null)
{
((IDisposable)memoryStream2).Dispose();
}
}
}
finally
{
if (memoryStream != null)
{
((IDisposable)memoryStream).Dispose();
}
}
}
使用新的 C# 8 Using 声明语法,第二个连续 using 语句的包含范围是什么?
TL;DR
在 C# 8 之前,有一个连续的 using 语句,如:
using(var disposable = new MemoryStream())
{
using(var secondDisposable = new StreamWriter(disposable))
{}
}
将扩展为如下内容 (My Source):
MemoryStream disposable = new MemoryStream();
try {
{
StreamWriter secondDisposable = new StreamWriter(disposable);
try{
{}
}
finally {
if(secondDisposable != null) ((IDisposable)secondDisposable).Dispose();
}
}
}
finally {
if(disposable != null) ((IDisposable)disposable).Dispose();
}
我知道还有另外两种可能的扩展,但它们大致都是这样
升级到 C# 8 后,Visual studio 提供了一个代码清理建议,我不确定我认为它是等效的建议。
将上面连续的using语句转换为:
using var disposable = new MemoryStream();
using var secondDisposable = new StreamWriter(disposable);
对我来说,这会将第二个的范围更改为与第一个相同的范围。在这种情况下,它可能会巧合地以正确的顺序处理流,但我不确定我是否喜欢依赖这种巧合。
为了清楚 VS 要求我做什么:我首先转换了内部(这是有道理的,因为内部仍然包含在外部的范围内)。然后我转换了外部(这在本地有意义,因为它仍然包含在方法的范围内)。这两个清理的组合是我很好奇的。
我也认识到我对此的想法可能略有(甚至是显着)偏差,但根据我今天的理解,这似乎不正确。我的评估中缺少什么?我错了吗?
我唯一能想到的是,在声明语句之后的所有内容的扩展中插入了某种隐式作用域。
In this case, It would probably coincidentally dispose of the streams in the correct order, but I'm not certain I like to rely on that happy coincidence.
The
using
locals will then be disposed in the reverse order in which they are declared.
所以,是的,他们已经考虑过了,并按照预期的顺序进行处理,就像之前的链接 using 语句一样。
我想看看使用它的真实功能。编译器不会随意更改分配或处置的范围或顺序。如果你有这样的方法:
void foo()
{
using(var ms = new MemoryStream())
{
using(var ms2 = new MemoryStream())
{
/// do something
}
}
}
然后 Dispose()
顺序无关紧要,因此编译器可以安全地安排它认为合适的事情。在其他情况下,顺序 是 很重要,编译器应该足够聪明以识别这一点。我不会在 "coincidence" 下提交 "good AST analysis."
说明Daminen的答案;当你有类似的方法时;
public void M()
{
using var f1 = new System.IO.MemoryStream(null,true);
using var f2 = new System.IO.MemoryStream(null,true);
using var f3 = new System.IO.MemoryStream(null,true);
}
IL将其转换为;
public void M()
{
MemoryStream memoryStream = new MemoryStream(null, true);
try
{
MemoryStream memoryStream2 = new MemoryStream(null, true);
try
{
MemoryStream memoryStream3 = new MemoryStream(null, true);
try
{
}
finally
{
if (memoryStream3 != null)
{
((IDisposable)memoryStream3).Dispose();
}
}
}
finally
{
if (memoryStream2 != null)
{
((IDisposable)memoryStream2).Dispose();
}
}
}
finally
{
if (memoryStream != null)
{
((IDisposable)memoryStream).Dispose();
}
}
}