我可以在 using 语句中安全地使用对象初始值设定项吗?
Can I safely use object initializers inside using statements?
我想知道 object initializers inside using statements 的使用是否会以某种方式阻止正确处理其中声明的资源,例如
using (Disposable resource = new Disposable() { Property = property })
{
// ...
}
我读到对象初始化器只不过是语法糖,编译器将其转换为类似于以下代码的内容:
MyClass tmp = new MyClass();
tmp.Property1 = 1;
tmp.Property2 = 2;
actualObjectYouWantToInitialize = tmp;
虽然我看起来是个糊涂的无知者,但我还是想请教一下。初始化对象是(据我所知)指向另一个对象的指针(据我所知也是一个指针)这一事实是否会干扰 using
语句完成的资源处理?
主要(唯一?)危险是如果设置 Property
失败(即抛出异常),则 resource
won't be Dispose
d。
using (Disposable resource = new Disposable() { Property = property })
{
// ...
}
通常 using
块中的异常是好的 - 因为 using
是 try .. finally
.[=32= 的语法糖]
这里的问题是,当 Property = property
正在执行你时 有效地 还没有 'inside' using
块。 这与构造函数抛出异常时发生的情况本质上没有什么不同。
finally
块将尝试 Dispose
的东西是 resource
- 但从未设置 resource
- resource
(如中所示您的 actualObjectYouWantToInitialize
示例)设置为 after the properties have all been set .
https://dotnetfiddle.net/vHeu2F 展示了这在实践中是如何发生的。请注意 Dispose
被记录了一次,即使有两个 using
块。
using System;
namespace Bob
{
public class Disposable : IDisposable
{
private int _property;
public int Property { get => _property; set => throw new Exception(); }
public void Dispose()
{
Console.WriteLine("Disposed");
}
}
public class Program
{
public static void Main()
{
Console.WriteLine("1");
using (var resource = new Disposable())
{
Console.WriteLine("2");
}
Console.WriteLine("3");
using (var resource = new Disposable() { Property = 1 })
{
Console.WriteLine("4");
}
Console.WriteLine("5");
}
}
}
CA2000 可能有助于检测此问题。
@mjwills 回答正确。详情如下:
public void M()
{
using (var x = new Test{Property = ""})
{
}
}
将生成以下 IL 代码:
.method public hidebysig
instance void M () cil managed
{
// Method begins at RVA 0x206c
// Code size 35 (0x23)
.maxstack 3
.locals init (
[0] class Test
)
IL_0000: nop
IL_0001: newobj instance void Test::.ctor()
IL_0006: dup
IL_0007: ldstr ""
IL_000c: callvirt instance void Test::set_Property(string)
IL_0011: nop
IL_0012: stloc.0
.try
{
IL_0013: nop
IL_0014: nop
IL_0015: leave.s IL_0022
} // end .try
finally
{
// sequence point: hidden
IL_0017: ldloc.0
IL_0018: brfalse.s IL_0021
IL_001a: ldloc.0
IL_001b: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0020: nop
// sequence point: hidden
IL_0021: endfinally
} // end handler
IL_0022: ret
} // end of method Test::M
可以看到在进入try之前调用了属性 setter 会导致在setter.[=12异常的情况下finally不会被调用=]
我想知道 object initializers inside using statements 的使用是否会以某种方式阻止正确处理其中声明的资源,例如
using (Disposable resource = new Disposable() { Property = property })
{
// ...
}
我读到对象初始化器只不过是语法糖,编译器将其转换为类似于以下代码的内容:
MyClass tmp = new MyClass();
tmp.Property1 = 1;
tmp.Property2 = 2;
actualObjectYouWantToInitialize = tmp;
虽然我看起来是个糊涂的无知者,但我还是想请教一下。初始化对象是(据我所知)指向另一个对象的指针(据我所知也是一个指针)这一事实是否会干扰 using
语句完成的资源处理?
主要(唯一?)危险是如果设置 Property
失败(即抛出异常),则 resource
won't be Dispose
d。
using (Disposable resource = new Disposable() { Property = property })
{
// ...
}
通常 using
块中的异常是好的 - 因为 using
是 try .. finally
.[=32= 的语法糖]
这里的问题是,当 Property = property
正在执行你时 有效地 还没有 'inside' using
块。 这与构造函数抛出异常时发生的情况本质上没有什么不同。
finally
块将尝试 Dispose
的东西是 resource
- 但从未设置 resource
- resource
(如中所示您的 actualObjectYouWantToInitialize
示例)设置为 after the properties have all been set .
https://dotnetfiddle.net/vHeu2F 展示了这在实践中是如何发生的。请注意 Dispose
被记录了一次,即使有两个 using
块。
using System;
namespace Bob
{
public class Disposable : IDisposable
{
private int _property;
public int Property { get => _property; set => throw new Exception(); }
public void Dispose()
{
Console.WriteLine("Disposed");
}
}
public class Program
{
public static void Main()
{
Console.WriteLine("1");
using (var resource = new Disposable())
{
Console.WriteLine("2");
}
Console.WriteLine("3");
using (var resource = new Disposable() { Property = 1 })
{
Console.WriteLine("4");
}
Console.WriteLine("5");
}
}
}
CA2000 可能有助于检测此问题。
@mjwills 回答正确。详情如下:
public void M()
{
using (var x = new Test{Property = ""})
{
}
}
将生成以下 IL 代码:
.method public hidebysig
instance void M () cil managed
{
// Method begins at RVA 0x206c
// Code size 35 (0x23)
.maxstack 3
.locals init (
[0] class Test
)
IL_0000: nop
IL_0001: newobj instance void Test::.ctor()
IL_0006: dup
IL_0007: ldstr ""
IL_000c: callvirt instance void Test::set_Property(string)
IL_0011: nop
IL_0012: stloc.0
.try
{
IL_0013: nop
IL_0014: nop
IL_0015: leave.s IL_0022
} // end .try
finally
{
// sequence point: hidden
IL_0017: ldloc.0
IL_0018: brfalse.s IL_0021
IL_001a: ldloc.0
IL_001b: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0020: nop
// sequence point: hidden
IL_0021: endfinally
} // end handler
IL_0022: ret
} // end of method Test::M
可以看到在进入try之前调用了属性 setter 会导致在setter.[=12异常的情况下finally不会被调用=]