為參數類型一樣返回類型不同的接口寫一個泛型方法


 

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類型,輸出參數T
            public 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~~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM