沒有擴展方法的日子
如果在開始使用C# 3之前你已經編寫了很多的C# 2代碼, 你應該看一下你的靜態類——它們中的大多數都是可以被轉換成擴展方法的候選. 這並不是說所有已經存在的靜態類都適合, 但你可以透過下面的特征來認出它:
另一種情況你可能也想可以使用接口來代替具體類型, 然後添加一些有用的方法到接口上面, 並通過接口來調用也可以實現類似的要求. 一個好的例子就是IList<T>. 如果它能夠對所有實現了IList的類型進行排序那應該會是非常美妙的事情, 然而這會要求所有的接口實現都要實現如何對它們自己排序, 這是非常可怕的. 當然從用戶的角度來看還是相當不錯的.
問題是IList<T>已經提供了一個完整的泛型排序例程, 但是我們卻不能將完整的實現放在該接口中(接口不能有實現). 我們當然可以將其指定為抽象類, 這樣就可以包含排序的實現了, 但.Net和C#只允許單繼承, 這將會嚴重限制那些從它繼承出去的類型. 擴展方法可以允許我們排序任何IList<T>的實現, 看起來就像是它本身就提供了這個功能一樣.
接下來讓我們來看一個例子, Stream類是.NET當中二進制通訊的基礎, Stream本身是一個抽象類, .Net Framework包含有幾個具體的實現類, 例如NetworkStream, FileStream, MemoryStream. 不幸的是, 有些個功能本該被包含在Stream當中, 然而實際上它們並沒有.
最常見的缺失功能是讀取一個流到內存當中並且保存為二進制數組的功能以及將一個流的內容拷貝到另外一個當中. 如果能將它們集中實現, 避免在不同的項目之間復制一些糟糕的實現無疑將會是是很不錯的主意. 下面的代碼提供了我們提到的這些額外的對Stream的功能要求.
1: using System.IO;
2: public static class StreamUtil
3: {
4: const int BufferSize = 8192;
5: public static void Copy(Stream input,Stream output)
6: {
7: byte[] buffer = new byte[BufferSize];
8: int read;
9: while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
10: {
11: output.Write(buffer, 0, read);
12: }
13: }
14: public static byte[] ReadFully(Stream input)
15: {
16: using (MemoryStream tempStream = new MemoryStream())
17: {
18: Copy(input, tempStream);
19: return tempStream.ToArray();
20: }
21: }
22: }
這個工具類本身並沒有太多東西需要解讀, 僅僅是為了方便之後引入擴展方法, 接下來看看我們如何使用這個工具類:
1: WebRequest request = WebRequest.Create("http://manning.com");
2: using (WebResponse response = request.GetResponse())
3: using (Stream responseStream = response.GetResponseStream())
4: using (FileStream output = File.Create("response.dat"))
5: {
6: StreamUtil.Copy(responseStream, output);
7: }
上面的代碼已經是很簡潔的了, StramUtil會自動循環接受流當中所有的數據, 其很好的完成了工作. 唯一的一點就是看起來不是那麼面向對象, 我們更喜歡能夠讓響應的數據流拷貝自己到輸出流當中, 就像MemeryStream自己的WriteTo方法一樣. 這不是什麼大問題, 僅僅是有一點丑陋.