2013-03-20 59 views
3

我与同事就使用声明的的适当范围争吵。这是有问题的方法。正确使用范围使用

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的声明,而其他认为,这仅仅是服务情况下需要使用,指明分数。我不知道我的理论是什么,但显然是错误的。哪一个?

+0

OrganizationService将被丢弃(如果它实现了IDisposed),但它将WhoAmI引用OrganizationService。因此,自从它被处置后,你将不会得到任何回报!您需要在使用声明中返回。 – Dave 2013-03-20 14:16:40

+2

如果变量whoAmI的整个范围是使用块(缺少括号会使事情在这里混淆一​​点),那么执行'return whoAmI'时甚至不会出现这种情况。 – 2013-03-20 14:18:09

+0

我没有看到它是如何重要的,如果你包括所有三行(声明,任务和返回) – 2013-03-20 14:19:42

回答

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(); 
} 
+1

不完全;我非常肯定''GetService()'分配发生在'try'内,因此如果线程_before_有异常,它会进入'try/finally'模块,它仍然会处理。编辑:见http://stackoverflow.com/a/2732078/1269654 – 2013-03-20 14:17:50

+0

是的,正确的方法是在'try-catch'块中写入这个.. – 2013-03-20 14:22:17

+1

不应该* service *的声明也在里面* try *? – 2013-03-20 14:26:28

1

using声明必须包含声明终止时应该处理的对象的声明。你的代码是正确的,只要OrganizationServiceProxy执行IDisposableWhoAmIResponse没有。

在有疑问的情况下,它通常是有用的使用块重写作为一个try-最后块:

OrganizationServiceProxy service = GetService(); 
try { 
    whoAmI = service.Execute(new WhoAmIRequest()) as WhoAmIResponse; 
} finally { 
    service.Dispose(); 
} 
+1

这不是编写try/finally等效块的正确方法。如果在线程_before_上抛出异常,它将进入try语句块,它永远不会处理该服务。请参阅:http://stackoverflow.com/a/2732078/1269654 – 2013-03-20 14:21:31

+0

“WhoAmIResponse”实现“IDisposable”与否正确无关紧要。 – 2013-03-20 14:32:31

5

两者是正确的。我倾向于写:

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; 
+0

这是IMO的唯一正确答案。 – 2013-03-20 14:22:51

+0

我有一对非常强烈的理由使用* whoAmI *声明在*使用*的范围之外的方法,所以除非它是明显错误的(你说它不是),否则我会继续这样做。谢谢。 – 2013-03-20 14:25:21

+1

出于兴趣:你的理由是什么?我写了一些非常有力的理由:-D – 2013-03-20 14:30:58

1

它为任何using声明的范围要尽可能小的最佳实践。只要OrganizationServiceProxy返回的对象在运行Dispose方法时没有丢弃,则指定的范围完全可以接受。

2

由于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的副作用,这会引发异常。

这是相当罕见的情况下(编辑:它应该指出的是获得接入的副作用和Dispose()抛出异常都被认为是不好的做法),我会建议如果它是在这里的话,那么你应该考虑这些是否正确。如果这些都是非问题(无副作用,不关心访问/处置的顺序),那么从长远来看,使用你和你的团队的感觉是更易于维护/可读的。