[concept:csharp/resource-cleanup]()使用IDispose介面來表示:某個物件的資源或其他程式狀態在該物件不再需要時必須被釋放或重設(而且光靠垃圾回收器無法達成這點,也無法提供所需的控制程度),而IDisposable.Dispose()方法正是這類清理作業最自然的所在。
還有一種結構,也就是using區塊,從呼叫端的角度來看,它能將所有的資源生命週期管理集中到單一敘述裡。
using (var file = new File("my_secrets"))
{
file.WriteSecret("xxxxxxx");
}
在上述範例中,與file相關的檔案系統資源會在離開using區塊時釋放回作業系統。
要讓這個模式運作,using敘述中的變數必須是實作IDisposable介面的參考型別,並且必須在其Dispose()方法中釋放資源、重設程式狀態。
C# 8 為這個模式帶來了一項改良。using敘述可以放在區塊的開頭。
using var drawingResource = some_provided_or_new_object;
try
{
drawingResource.DrawSomething();
}
catch (Exception)
{
throw;
}
Java 開發者或許會認出,這與 Java 7 引進的自動資源管理機制相當類似。
你又回來處理 object-relational-mapping 裡介紹過的 ORM(物件關聯對應)系統了。
我們對 ORM 的使用情形分析顯示,有 95% 的交易都是在同一個呼叫方法內執行的,因此決定改為由單一的 ORM 方法來開啟、寫入並提交交易,這樣會更合適。
資料庫提供了以下幾個執行個體方法:
Database.BeginTransaction() 會在資料庫上開始一筆交易。Database.Write(string data) 會在交易中將資料寫入資料庫。如果收到錯誤的資料,就會擲回例外。若在尚未呼叫BeginTransaction()的情況下嘗試呼叫這個方法,也會擲回例外。如果成功,資料庫的內部狀態會變成DataWritten。Database.EndTransaction() 會將交易提交到資料庫。如果無法關閉交易,或是先前沒有呼叫Database.BeginTransaction(),就可能擲回例外。Database.Dispose()可以在交易期間擲回例外時清理資料庫。這會將資料庫的狀態變成Closed。實作Orm.Write()方法,開始一筆資料庫交易、寫入傳入的資料,然後提交交易。所有例外,包括InvalidOperationException,都應該直接傳遞給呼叫端,不要記錄例外,也不要做任何其他介入。
Database db = new Database();
Orm orm = new Orm(db);
orm.Write("good write");
// => database has an internal state of State.Closed
orm.Write("bad write");
// => an exception is thrown but database is left with an internal state of State.Closed
orm.Write("bad commit");
// => an exception is thrown but database is left with an internal state of State.Closed
自從Orm.Write()推出至今已經過了幾個月,團隊內部也出現了分歧。許多開發者希望 ORM 能在擲回例外時自行處理錯誤,並單純向呼叫端回傳成功與否的指示。
請實作Orm.WriteSafely()方法,開始一筆資料庫交易、寫入傳入的資料並提交交易(做法同上)。如果_寫入_成功,它應該回傳true,否則回傳false。不要嘗試記錄例外。
Database db = new Database();
Orm orm = new Orm(db);
orm.WriteSafely("good write");
// => true
orm.WriteSafely("bad write");
// => false
orm.WriteSafely("bad commit");
// => false