正确使用范围

时间:2013-03-20 14:12:08

标签: c# scope using-statement

我和一位同事争论使用语句的适当范围。这是有问题的方法。

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 实例需要使用。我不是说我的理论是哪一个,但显然是错误的。哪个?

5 个答案:

答案 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实现IDisposableWhoAmIResponse没有实现,您的代码就是正确的。

如果有疑问,将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();
}