使用的正确范围
本文关键字:范围 | 更新日期: 2023-09-27 18:04:57
我和一位同事就using语句的适当范围发生了争执。这就是有问题的方法。
public Guid IsServerReachable()
{
try
{
WhoAmIResponse whoAmI;
using (OrganizationServiceProxy service = GetService())
whoAmI = service.Execute(new WhoAmIRequest()) as WhoAmIResponse;
return whoAmI.UserId;
}
catch { return Guid.Empty; }
}
我们中的一个声称using语句应该包括whyAmI的声明,而另一个则认为只有服务实例需要使用ified。我不知道哪一个是我的理论,但其中一个显然是错误的。哪一个
两者都是正确的。我倾向于这样写:
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
,则必须编写以下内容才能自动释放两者:
using (OrganizationServiceProxy service = GetService())
using (WhoAmIResponse whoAmI = service.Execute(new WhoAmIRequest()) as WhoAmIResponse)
return whoAmI.UserId;
由于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
而声明使用块中的所有内容:
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
。
这些情况相当罕见(EDIT:需要注意的是,get access副作用和Dispose()
抛出异常都被认为是不良做法(,我建议如果这里是,那么您应该考虑这些情况的正确性。如果这些都不是问题(没有副作用,也不关心访问/处理顺序(,那么从长远来看,使用您和您的团队认为更易于维护/可读的内容。
using
语句必须包含在语句终止时应该处理的对象的声明。只要OrganizationServiceProxy
实现了IDisposable
,而WhoAmIResponse
没有实现,您的代码就是正确的。
如果有疑问,通常将using块重写为try-filly块是有用的:
OrganizationServiceProxy service = GetService();
try {
whoAmI = service.Execute(new WhoAmIRequest()) as WhoAmIResponse;
} finally {
service.Dispose();
}
任何using
语句的范围尽可能小是最佳实践。只要OrganizationServiceProxy
在运行Dispose
方法时返回的对象没有被处理,那么所指示的范围是完全可以接受的。
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();
}