3

我与一位同事就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的声明,而另一个人则认为只有服务实例需要被使用化。我没有说哪个是我的理论,但一个显然是错误的。哪一个?

4

5 回答 5

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,您必须编写以下内容才能自动释放两者:

    using (OrganizationServiceProxy service = GetService())
    using (WhoAmIResponse whoAmI = service.Execute(new WhoAmIRequest()) as WhoAmIResponse)
        return whoAmI.UserId;
于 2013-03-20T14:19:29.320 回答
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()抛出异常都被认为是不好的做法),我建议如果是这种情况,那么您应该考虑这些以确保正确性。如果这些不是问题(没有副作用并且不关心访问/处置的顺序),那么从长远来看,使用您和您的团队认为更易于维护/可读的东西。

于 2013-03-20T14:53:43.097 回答
1

using语句必须包含在语句终止时应该被释放的对象的声明。只要OrganizationServiceProxy实现IDisposableWhoAmIResponse不实现,您的代码就是正确的。

如有疑问,将 using 块重写为 try-finally 块通常很有用:

OrganizationServiceProxy service = GetService();
try {
   whoAmI = service.Execute(new WhoAmIRequest()) as WhoAmIResponse;
} finally {
    service.Dispose();
}
于 2013-03-20T14:15:51.120 回答
1

任何using语句的范围尽可能小是最佳实践。只要您返回的对象OrganizationServiceProxy在运行该Dispose方法时没有被丢弃,指示的范围是完全可以接受的。

于 2013-03-20T14:20:12.427 回答
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();
}
于 2013-03-20T14:16:53.950 回答