[英]using statement with fluent api will not call dispose if throw occurs in fluent method chain
將 using 語句與可能拋出的流暢 api 結合使用時,降低的代碼將永遠不會正確調用 dispose。
如果我有以下 class 公開流暢的界面:
public class Wrapper : IDisposable
{
private bool _isAdded;
public Wrapper Add()
{
_isAdded = true;
return this;
}
public void Dispose() => Console.WriteLine("dispose called");
public Wrapper ThrowIfAdded() => _isAdded ? throw new Exception() : this;
}
我用以下方式調用它:
using var willNotDispose = new Wrapper().Add().ThrowIfAdded();
降低的代碼將導致在流暢的方法鏈完成后發生 Dispose 調用。
Wrapper willNotDispose = new Wrapper().Add().ThrowIfAdded();
try
{
}
finally
{
if (willNotDispose != null)
{
((IDisposable)willNotDispose).Dispose();
}
}
或者,如果對.ThrowIfAdded()
的調用是在初始using
聲明之外完成的,
using var willDispose = new Wrapper().Add();
willDispose.ThrowIfAdded();
降低的代碼按預期生成。
Wrapper willDispose = new Wrapper().Add();
try
{
willDispose.ThrowIfAdded();
}
finally
{
if (willDispose != null)
{
((IDisposable)willDispose).Dispose();
}
}
雖然我理解為什么會發生這種情況,但這是不可取的。 有沒有辦法強制前者初始化編譯為后者? 理想情況下,它將是編譯器提示的一個屬性或形式,它會導致:
Wrapper willDispose = default;
try
{
willDispose = new Wrapper().Add().ThrowIfAdded();
}
finally
{
if (willDispose != null)
{
((IDisposable)willDispose).Dispose();
}
}
我本來希望原始示例首先編譯到。
正如評論中所指出的,已有的指導是,當在構造函數中拋出異常時,應該顯式處理它並清理資源。
這延伸到CA2000
分析,該分析指出:
當僅受一個異常處理程序保護的構造函數嵌套在 using 語句的獲取部分時,外部構造函數中的失敗可能導致嵌套構造函數創建的 object 永遠不會關閉。 在以下示例中,StreamReader 構造函數中的失敗可能導致 FileStream object 永遠不會關閉。 在這種情況下,CA2000 標記違反規則。
using (StreamReader sr = new StreamReader(new FileStream("C:/myfile.txt", FileMode.Create)))
{ ... }
雖然拋出異常的 Fluent API 不是顯式構造函數或拋出異常的嵌套構造函數,但應將其視為相同,因為 object 將在 try/finally 塊之外創建和變異。
因此,任何可以拋出的方法都必須先調用 dispose,然后才能傳播異常。
public class Wrapper : IDisposable
{
private bool _isDisposed;
private bool _isAdded;
public Wrapper Add()
{
_isAdded = true;
return this;
}
public void Dispose()
{
if (_isDisposed)
{
return;
}
_isDisposed = true;
Console.WriteLine("dispose called");
}
public Wrapper ThrowIfAdded()
{
if (_isAdded)
{
Dispose();
throw new Exception();
}
return this;
}
}
這正確地確保了在調用.Added()
的情況下, .ThrowIfAdded()
將在拋出之前進行處理。
如果未調用.Added()
,則實例將按預期放置在塊的末尾。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.