Jeffrey Zhao真是神一樣的存在,伊太結棍了(上海話),每次看他的博客得使勁使勁使勁地啃。本篇源於Jeffery Zhao的"逆泛型執行器"這篇文章。該文提到了為以下的接口寫一個泛型方法:
public interface IRecord{string GetString(string field);int GetInt(string field);long GetLong(string field);}
先來實現該接口:
public class MyRecord : IRecord{public string GetString(string field){return field + "--added string";}public int GetInt(string field){return int.Parse(field + "1");}public long GetLong(string field){return long.Parse(field);}}
通常,在客戶端這樣調用:
static void Main(string[] args){MyRecord myRecord = new MyRecord();Console.WriteLine(myRecord.GetString("hello"));Console.WriteLine(myRecord.GetInt("1"));Console.WriteLine(myRecord.GetLong("2"));Console.ReadKey();}
以上,對於IRecord接口的各個方法而言,它們處在同一個接口,有相同的方法參數,唯一不同的是返回類型,看來有必要請出泛型了。
對於接口方法來說,接口就是它們的"天",它們得"一輩子"待在這里。而對於泛型而言,它以更高的視角來俯視接口和它的方法們。
可能,我們需要這樣一個針對IRecord接口的泛型方法:SomeExtenion.Get<T>(IRecord record, string str)。
這里的T是什么,返回類型就是什么, 如果T是string類型,那就返回string類型,等等。輸入參數就IRecord是接口和string類型。
如何做呢?
有這樣的一個類的輪廓模模糊糊地出現在了腦海里:
public static class RecordExtensions{public static T Get<T>(IRecord record, string field){}}
可是問題來了,如何返回T類型呢?
Jeffrey Zhao給出了答案!
.NET 泛型的奇妙之處便在於其“動態”及“區分”。“動態”在於它可以於運行時進行具體化(相對於 C++ 里的“靜態”),不過目前的問題不涉及這點。而“區分”則意味着不同的具體泛型參數,在 .NET 中都是不同的類型,擁有完全分離的元數據,例如方法表(Method Table),以及靜態字段等等。
再次回到問題的本質:接口調用方法1,輸入string類型參數,返回某個類型1;接口調用方法2,輸入string類型參數,返回某個類型2......這不就是一個委托,這不就是Func<IRecord, string, T>嗎?
如果我們能把以下存儲到某個地方該多好啊!
(irecord, field) => irecord.GetString(field)(irecord, field) => irecord.GetInt(field)(irecord, field) => irecord.GetLong(field)
當Get<T>調用的時候再把這些泛型委托取出來。
Jeffery Zhao又給出了答案!
一個內部類 Cache,它起到了緩存的作用。
於是,再完善一下RecordExtensions這個類。
public static class RecordExtensions{private static class Cache<T>{//委托類型字段,輸入參數是IRecord和string類型,輸出參數Tpublic static Func<IRecord, string, T> Get;}//構造函數中就往緩存中存數據static RecordExtensions(){Cache<string>.Get = (irecord, field) => irecord.GetString(field);Cache<int>.Get = (irecord, field) => irecord.GetInt(field);Cache<long>.Get = (irecord, field) => irecord.GetLong(field);}public static T Get<T>(IRecord record, string field){return Cache<T>.Get(record, field);}}
以上,在靜態類的靜態構造函數中,把委托賦值給靜態類中作為緩存的靜態類Cache的委托類型的字段Get,由於.NET的“區分”性,緩存中的泛型委托擁有完全分離的元數據,當調用Get<T>方法的時候,會從緩存中取出對應的委托。
最后,在客戶端這樣調用:
static void Main(string[] args){MyRecord myRecord = new MyRecord();Console.WriteLine(myRecord.GetString("hello"));Console.WriteLine(myRecord.GetInt("1"));Console.WriteLine(myRecord.GetLong("2"));Console.WriteLine(RecordExtensions.Get<string>(myRecord, "hello"));Console.WriteLine(RecordExtensions.Get<int>(myRecord, "1"));Console.WriteLine(RecordExtensions.Get<long>(myRecord, "2"));Console.ReadKey();}
通過調用接口方法和泛型方法,得到的結果是一樣一樣滴。
Thanks, Jeffrey Zhao~~