当前位置: 代码迷 >> C# >> 替参数类型一样返回类型不同的接口写一个泛型方法
  详细解决方案

替参数类型一样返回类型不同的接口写一个泛型方法

热度:65   发布时间:2016-05-05 04:00:59.0
为参数类型一样返回类型不同的接口写一个泛型方法

 

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~~

  相关解决方案