[C#.NET] 使用 using 或 try/finally 清理資源
在.NET世界裡若有用了托管資源,可以將類別繼承IDisposable來釋放資源或是實作解構子,若類別有繼承IDisposable,除了使用try/finally之外還可以使用using關鍵字;解構子的生命週期太長,可能要等到所有應用程式關閉才會觸發,所以本篇討論的是繼承IDisposable介面的方法。
在很多時候很多人習慣會這樣寫,者只是一個簡單的讀檔,然後回傳讀到的結果
public string ReaderLine(string FileName)
{
if (!File.Exists(FileName))
return null;
Stream stream = null;
StreamReader reader = null;
string line="";
string result="";
try
{
stream = File.Open(FileName, FileMode.Open);
reader = new StreamReader(stream);
while ((line = reader.ReadLine()) != null)
{
result += line + Environment.NewLine;
}
reader.Close();
stream.Close();
return result;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return null;
}
}
這樣看起來很好,有判斷檔案存在與否,也補捉例外了,但資源的釋放呢??假若try區段發生exception,reader.Close跟stream.Close永遠不會執行到了,資源也沒有釋放,等到下次其它應用程式或是自己的程式要用的時候,檔案一直跳出被鎖定,你可能會為了找出被誰鎖定而苦惱,甚至花了不少時間還不知道為什麼。
下列程式碼,我故意製造轉型失敗
public string ReaderLine(string FileName)
{
if (!File.Exists(FileName))
return null;
Stream stream = null;
StreamReader reader = null;
string line="";
string result="";
try
{
stream = File.Open(FileName, FileMode.Open);
reader = new StreamReader(stream);
while ((line = reader.ReadLine()) != null)
{
result += line + Environment.NewLine;
}
string a = "999";
byte b = byte.Parse(a);
reader.Close();
stream.Close();
return result;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return null;
}
}
當第一次呼叫這個方法時會出現轉型失敗的例外,第二次以後就會出現資源正在被其他程序使用當中
為了確保資源一定會被釋放我們可以改成以下,將釋放的程式碼寫在finally區段裡面。
public string ReaderLine(string FileName)
{
if (!File.Exists(FileName))
return null;
Stream stream = null;
StreamReader reader = null;
string line="";
string result="";
try
{
stream = File.Open(FileName, FileMode.Open);
reader = new StreamReader(stream);
while ((line = reader.ReadLine()) != null)
{
result += line + Environment.NewLine;
}
string a = "999";
byte b = byte.Parse(a);
return result;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return null;
}
finally
{
reader.Close();
stream.Close();
}
}
或是用using包起來
public string ReaderLine(string FileName)
{
if (!File.Exists(FileName))
return null;
string line = "";
string result = "";
try
{
using (Stream stream = File.Open(FileName, FileMode.Open))
{
using (StreamReader reader = new StreamReader(stream))
{
while ((line = reader.ReadLine()) != null)
{
result += line + Environment.NewLine;
}
string a = "999";
byte b = byte.Parse(a);
return result;
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return null;
}
}
就算例外不幸發生了,這樣也能確保Close方法一定會被執行到,不管程式碼被呼叫器了幾次,在有例外發生的情況下,所使用的資源被釋放了,也不會有檔案被鎖定的例外發生了
除了資源的釋放,我也常用在執行緒的釋放
Monitor.Enter(_thisLock);
try
{
//TODO
}
finally
{
Monitor.Exit(_thisLock);
}
後記:
不只資源的釋放,只要是一定要做的動作,我們都可以用把它寫在finally裡,用來保証一定會執行。using若是要跟finnally選一個,個人還是比較習慣用finally來釋放資源,若用了太多層的using除了不好閱讀之外,也不容易維護。
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET