我可以在 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 Disposed

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

通常 using 块中的异常是好的 - 因为 usingtry .. 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不会被调用=]