C# 我可以安全地在using语句中使用对象初始值设定项吗?
我想知道的是,使用是否会以某种方式阻止正确处理它们内部声明的资源,例如C# 我可以安全地在using语句中使用对象初始值设定项吗?,c#,using-statement,object-initializers,C#,Using Statement,Object Initializers,我想知道的是,使用是否会以某种方式阻止正确处理它们内部声明的资源,例如 using (Disposable resource = new Disposable() { Property = property }) { // ... } 我已经了解到,对象初始值设定项只是synctatic sugar,编译器将其转换为类似于以下代码的内容: MyClass tmp = new MyClass(); tmp.Property1 = 1; tmp.Property2 = 2; actualO
using (Disposable resource = new Disposable() { Property = property })
{
// ...
}
我已经了解到,对象初始值设定项只是synctatic sugar,编译器将其转换为类似于以下代码的内容:
MyClass tmp = new MyClass();
tmp.Property1 = 1;
tmp.Property2 = 2;
actualObjectYouWantToInitialize = tmp;
即使我看起来像一个糊涂的无知者,我还是想要求澄清。初始化对象(据我所知)是指向另一个对象的指针(据我所知,也是指针)这一事实是否会干扰使用语句对资源的处理?主要(仅)危险在于,如果设置属性失败(即引发异常),然后资源
通常情况下,using
块中的异常是可以处理的,因为using
是try的语法糖。。最后
这里的问题是,当Property=Property
执行时,您还没有使用块“进入”。这与构造函数抛出异常时发生的情况基本相同
最后
块将尝试处理
的是资源
——但是资源
从未设置过——资源
(如actualObjectYouWantToInitialize
示例所示)已设置
展示了这在实践中是如何发生的。请注意,Dispose
只记录了一次,即使有两个使用
块
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");
}
}
}
可能有助于检测此问题。@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中出现异常时最终无法调用。@TimSchmelter我刚刚用以下类测试了这一点:public class Test:IDisposable{public string property{get=>“Hello”;set=>throw new exception();}public void Dispose(){Console.WriteLine(“Disposed”);}}
。当运行using(var x=new Test(){Property=“Test”})
@JonathonChase时没有调用Dispose,我认为这是因为编译器使用将转换为的try finally
在调用Dispose()
之前检查资源是否为null
,初始化失败时就是这种情况。@StackLloyd许多人都设置了资源
变量的心智模型,然后在其上设置了属性(这是错误的)。这种心理模型可能就是一些人认为对象将被处理的原因。@StackLloyd通过使用对象初始值设定项语法,首先创建对象,然后设置属性。因此创建了实例(需要处理),但没有(如您所说)分配给将在finally块中处理的变量。“那么你有漏洞了。”勒内·沃格特明白了。那太丢脸了。。。总的来说,这意味着内部使用语句的对象初始值设定项应该被视为不好的做法。@StackLloyd我不确定我会称之为不光彩。这是两个句法特征如何相交的副作用(或结果)。如果你仔细想想,它是非常有意义的(我的意思是它可能不是你想要的——但是考虑到这两个特性是如何独立工作的,它们一起玩的方式是有意义的)。此外,在实践中,这很少是一个问题。大多数属性设置程序不会抛出异常。@mjwills虽然这是完全正确的,但我认为应该以某种方式警告C#程序员此类交互,因为它们几乎从来都不是问题,直到它们不知不觉地成为问题。在这里发布之前,我在官方文件上搜索了答案,但没有结果。
.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