五、以自定義方式處理
這是有益的當你去考慮如果TextReader沒有實現IDisposable接口將會出現什麼情況。這篇教程從此處開始將知道我們如何在我們自己的類裡實現Dispose的處理。一種方式是使用對象適配器(Object Adapter)模式。例如:
public sealed class AutoTextReader : IDisposable
{
public AutoTextReader(TextReader target)
{
// PreCondition(target != null);
adaptee = target;
}
public TextReader TextReader
{
get { return adaptee; }
}
public void Dispose()
{
adaptee.Close();
}
private readonly TextReader adaptee;
}
你可以這樣使用你自己的類:
using (AutoTextReader scoped = new AutoTextReader(file.OpenText()))
{
scoped.TextReader.Read(source, 0, length);
}
你能夠使用隱式轉換操作符使問題變得更簡單一些:
public sealed class AutoTextReader : IDisposable
{
...
public static implicit operator AutoTextReader(TextReader target)
{
return new AutoTextReader(target);
}
...
}
這將允許你這樣使用:
using (AutoTextReader scoped = file.OpenText())
{
scoped.TextReader.Read(source, 0, length);
}
struct :另一種選擇
AutoTextReader有意使用為密封類,就想它的名字建議的,以用作本地變量。使用一個結構來代替類更加有意義:
public struct AutoTextReader : IDisposable
{
// exactly as before
}
使用一個結構代替類也提供給你幾種自由優化。既然一個結構是一個值類型,它能構一直都不是空值。這意味著編譯器必須對生成的finally程序塊做空值的檢測。並且,既然你不能繼承於一個結構,它的運行時將和編譯時的類型一致。這意味著編譯器一般在生成的finally程序塊中做強制轉換並因而避免了一次裝箱操作(特別的,如果Dispose是一個公開的隱式接口實現而不是一個不公開的顯示接口實現,這將避免強制轉換)。
換句話,就是這樣:
using (AutoTextReader scoped = file.OpenText())
{
scoped.TextReader.Read(source, 0, length);
}
被轉換為:
{
AutoTextReader scoped = new file.OpenText();
try
{
scoped.TextReader.Read(source, 0, length);
}
finally
{
scoped.Dispose();
}
}
由此,我更喜歡使用using語句代替finally程序開來處理。事實上,using語句解決方式相較於開始的 “理想“版本還有如下額外的幾個優點,一個using語句:
·運行中,它能夠一直釋放資源
·是一個擴展機制。它允許你創建一個資源釋放的集合。創建你自己的資源釋放類例如 AutoTextReader是容易的。
·允許你將資源獲取和資源釋放配對使用。釋放資源最好的時刻就是你獲得資源的一刻。就像如 果你從圖書館借了一本書,你能在你借的時候被告知什麼時候歸還。】
·根據句法構造,能夠清楚的告訴你你正在使用一個資源。
·為擁有資源的變量創造一個范圍。仔細觀察對using語句的編譯器轉換,你將發現它聰明的使用了一對外部括號。
using (AutoTextReader scoped = file.OpenText())
{
scoped.TextReader.Read(source, 0, length);
}
scoped.TextReader.Close(); // scoped is not in scope here
這是對C++中依據條件聲明的追溯。允許你限制變量使用的范圍,僅僅在這個范圍內,變量是有用的,當變量能夠使用,它只能存在於這個范圍內。