2013-03-20 42 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()拋出異常都被認爲是不好的做法),我會建議如果它是在這裏的話,那麼你應該考慮這些是否正確。如果這些都是非問題(無副作用,不關心訪問/處置的順序),那麼從長遠來看,使用你和你的團隊的感覺是更易於維護/可讀的。