我和一位同事争论使用语句的适当范围。这是有问题的方法。
public Guid IsServerReachable()
{
try
{
WhoAmIResponse whoAmI;
using (OrganizationServiceProxy service = GetService())
whoAmI = service.Execute(new WhoAmIRequest()) as WhoAmIResponse;
return whoAmI.UserId;
}
catch { return Guid.Empty; }
}
我们中的一个人声称使用语句应该包含 whyAmI 的声明,而另一个则认为它只是 service 实例需要使用。我不是说我的理论是哪一个,但显然是错误的。哪个?
答案 0 :(得分:6)
两者都是正确的。我倾向于写这个:
public Guid IsServerReachable()
{
try
{
using (OrganizationServiceProxy service = GetService())
{
WhoAmIResponse whoAmI = service.Execute(new WhoAmIRequest()) as WhoAmIResponse;
return whoAmI.UserId;
}
}
catch { return Guid.Empty; }
}
这对whoAmI
是否被处置没有任何影响 - 唯一被自动处理的是service
。
如果WhoAmIResponse
也是IDisposable
,您必须编写以下内容以自动同时发布:{/ p>
using (OrganizationServiceProxy service = GetService())
using (WhoAmIResponse whoAmI = service.Execute(new WhoAmIRequest()) as WhoAmIResponse)
return whoAmI.UserId;
答案 1 :(得分:3)
由于whoAmI
的声明在这种情况下在性能/范围方面没有任何影响。它真正归结为whoAmI.UserId
的财产访问权限也应包括在using
中。从IL的角度来看,两者之间唯一的功能区别在于调用OrganizationalServiceProxy.Dispose
方法的顺序以及访问WhoAmIResponse.UserId
的时间。
(编辑:我认为try/catch
如何处理以返回默认值并不存在任何实际问题,这似乎不是问题的一部分,因此也省略了)
在您发布的代码中(简化以使IL更清晰):
public Guid IsServerReachableOutsideUsingScope()
{
WhoAmIResponse whoAmI;
using(var service = new Service())
whoAmI = service.Execute();
return whoAmI.UserId;
}
IL的结果:
IL_0000: newobj UserQuery+Service..ctor
IL_0005: stloc.1 // service
IL_0006: ldloc.1 // service
IL_0007: callvirt UserQuery+Service.Execute
IL_000C: stloc.0 // whoAmI
IL_000D: leave.s IL_0019
IL_000F: ldloc.1 // service
IL_0010: brfalse.s IL_0018
IL_0012: ldloc.1 // service
IL_0013: callvirt System.IDisposable.Dispose
IL_0018: endfinally
IL_0019: ldloc.0 // whoAmI
IL_001A: callvirt UserQuery+WhoAmIResponse.get_UserId
IL_001F: ret
然后在using块中声明所有内容:
public Guid IsServerReachableWithinUsingScope()
{
using(var service = new Service())
{
WhoAmIResponse whoAmI = service.Execute();
return whoAmI.UserId;
}
}
产生IL:
IL_0000: newobj UserQuery+Service..ctor
IL_0005: stloc.0 // service
IL_0006: ldloc.0 // service
IL_0007: callvirt UserQuery+Service.Execute
IL_000C: stloc.1 // whoAmI
IL_000D: ldloc.1 // whoAmI
IL_000E: callvirt UserQuery+WhoAmIResponse.get_UserId
IL_0013: stloc.2 // CS$1$0000
IL_0014: leave.s IL_0020
IL_0016: ldloc.0 // service
IL_0017: brfalse.s IL_001F
IL_0019: ldloc.0 // service
IL_001A: callvirt System.IDisposable.Dispose
IL_001F: endfinally
IL_0020: ldloc.2 // CS$1$0000
IL_0021: ret
如果重要您的服务不在访问该属性之前被处置(例如在NHibernate延迟加载的集合的上下文中),那么该顺序绝对重要。如果没关系,那么最大的问题应该是你和你的团队最关心的问题。如果您不介意混合和匹配using
来电,那么有些人有大括号,有些则没有,那么继续你所拥有的。
可能如果访问WhoAmIResponse.UserId
有副作用,可能要考虑的是异常处理的顺序。 如果您的服务上的Dispose
调用引发异常,则在原始代码(IsServerReachableOutsideUsingScope
)中,它将永远不会访问您的属性,因此从未执行其副作用。在第二个代码块(IsServerReachableWithinUsingScope
)中,它将访问并执行使用UserId
属性的副作用,然后运行Dispose
抛出一个异常。
这些是非常罕见的情况(编辑:应该注意的是,获取访问副作用和Dispose()
抛出异常都被视为不良做法),我建议如果它就是这里的情况,那么你应该考虑这些是正确的。如果这些都不是问题(没有副作用,也不关心访问/处置的顺序),那么从长远来看,使用您和您的团队认为更易于维护/可读的内容。
答案 2 :(得分:1)
using
语句必须包含语句终止时应该处理的对象的声明。只要OrganizationServiceProxy
实现IDisposable
而WhoAmIResponse
没有实现,您的代码就是正确的。
如果有疑问,将using块重写为try-finally块通常很有用:
OrganizationServiceProxy service = GetService();
try {
whoAmI = service.Execute(new WhoAmIRequest()) as WhoAmIResponse;
} finally {
service.Dispose();
}
答案 3 :(得分:1)
最佳做法是使任何using
语句的范围尽可能小。只要OrganizationServiceProxy
返回的对象在运行Dispose
方法时没有被丢弃,指定的范围就完全可以接受。
答案 4 :(得分:0)
using (OrganizationServiceProxy service = GetService())
whoAmI = service.Execute(new WhoAmIRequest()) as WhoAmIResponse;
等同于:
OrganizationServiceProxy service = GetService();
try
{
whoAmI = service.Execute(new WhoAmIRequest()) as WhoAmIResponse;
}
finally
{
if (myRes!= null)
// Call the object's Dispose method.
((IDisposable)service).Dispose();
}