簡體   English   中英

如果在流利的方法鏈中發生拋出,則使用流利的 api 語句將不會調用處置

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM