我可以在using语句中安全地使用对象初始化程序吗?

时间:2019-07-25 13:55:20

标签: c# using-statement object-initializers

我想知道使用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语句对资源的处理?

2 个答案:

答案 0 :(得分:6)

主要的(唯一的)危险是,如果设置Property失败(即引发异常),则resource won't be Disposed

using (Disposable resource = new Disposable() { Property = property })
{
   // ...
}
using块内

通常异常很好-因为usingtry .. finally的语法糖。

这里的问题是,Property = property正在有效执行您时还没有进入using块内部。 与构造器引发异常时发生的事情本质上没有什么不同。

finally块将尝试尝试Dispose的事物是resource-但从未设置resource-resource(如您的{ {1}}示例)设置为after the properties have all been set

https://dotnetfiddle.net/vHeu2F显示了在实践中可能如何发生。请注意,即使有两个actualObjectYouWantToInitialize块,Dispose也被记录一次。

using

CA2000可能有助于发现此问题。

答案 1 :(得分:2)

@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之前调用了属性设置器,这将导致在设置器发生异常的情况下最终不被调用。