.net基础—泛型


泛型

泛型在 .NET Framework 2.0 中首次引入,它本质上是一个“代码模板”,可让开发者定义类型安全数据结构,无需处理实际数据类型。 例如,List 是一个可以声明的泛型集合,可与 List、List 或 List 等任何类型结合使用。

为方便理解泛型的作用,让我们看看添加泛型之前和之后的一个特定类:ArrayList。 在 .NET Framework 1.0 中,ArrayList 元素属于 Object 类型。 添加到集合的任何元素都会以静默方式转换为 Object。 从列表读取元素时,会发生相同的情况。 此过程称为装箱和取消装箱,它会影响性能。 但除了性能之外,在编译时无法确定列表中的数据的类型,这会形成一些脆弱的代码。 泛型解决了此问题,它可以定义每个列表实例将要包含的数据类型。 例如,只能将整数添加到 List,只能将人员添加到 List

泛型还可以在运行时使用。 运行时知道你要使用的数据结构类型,并可以更高效地将数据结构存储在内存中。

泛型的性能

通过执行一亿次三种不同类型方法:确定参数类型的方法、Object参数类型的方法、 泛型参数类型的方法,来看一下它们的耗时情况。

 1 class Program
 2     {
 3         static void Main(string[] args)
 4         {          
 5             int testVal = 123;
 6             long commonSecond = 0;
 7             long objectSecond = 0;
 8             long genericSecond = 0;
 9 
10             {
11                 // 调用确定参数类型的方法的耗时
12                 Stopwatch watch = new Stopwatch();
13                 watch.Start();
14                 for (int i = 0; i < 100000000; i++)
15                 {
16                     ShowInt(testVal);
17                 }
18                 watch.Stop();
19                 commonSecond = watch.ElapsedMilliseconds;
20             }
21             {
22                 // 调用Object参数类型的方法的耗时
23                 Stopwatch watch = new Stopwatch();
24                 watch.Start();
25                 for (int i = 0; i < 100000000; i++)
26                 {
27                     ShowObject(testVal);
28                 }
29                 watch.Stop();
30                 objectSecond = watch.ElapsedMilliseconds;
31             }
32             {
33                 //调用泛型参数类型的方法的耗时
34                 Stopwatch watch = new Stopwatch();
35                 watch.Start();
36                 for (int i = 0; i < 100000000; i++)
37                 {
38                     Show<int>(testVal);
39                 }
40                 watch.Stop();
41                 genericSecond = watch.ElapsedMilliseconds;
42             }
43             Console.WriteLine("commonSecond = {0} \n objectSecond = {1} \n genericSecond = {2} ",
44                 commonSecond, objectSecond, genericSecond);
45 
46         }
47 
48         /// 
49         /// 确定参数类型的方法
50         /// 
51         /// 
52         private static void ShowInt(int valParam)
53         {
54 
55         }
56         /// 
57         /// Object参数类型的方法(类型不确定通过用obj 来实现通用)
58         ///        
59         private static void ShowObject(int objParam)
60         {
61 
62         }
63         /// 
64         /// 泛型参数类型的方法
65         /// 
66         private static void Show(int tParam)
67         {
68 
69         }
70 
71     }

上面代码的执行效果:

通过上图可以得出:确定参数类型的方法和 泛型参数类型的方法相差不是很大,Object参数类型的方法的方法性能最差,Object慢的原因是多了一个装箱操作。

协变逆变

在C# 4.0之前,所有的泛型类型都是不变量——即不支持将一个泛型类型替换为另一个泛型类型,即使它们之间拥有继承关系,简而言之,在C# 4.0之前的泛型都是不支持协变和逆变的。

C# 4.0通过两个关键字:out和in来分别支持以协变和逆变的方式使用泛型。

    class Program
    {
        static void Main(string[] args)
        {
            List birdList1 = new List();
            // vs提示错误
            //List birdList2 = new List();            
            List birdList3 = new List().Select(c => (Bird)c).ToList();


            //协变  IEnumerable既可以接受父类也可以接受子类
            IEnumerable birdList4 = new List();
            IEnumerable birdList5 = new List();

            //逆变 ICustomerListIn既可以接受父类也可以接受子类
            ICustomerListIn customerList1 = new CustomerListIn();
            ICustomerListIn customerList2 = new CustomerListIn();

        }
    }

    class Bird
    { 
    
    }
    class Sparrow : Bird
    { 
    
    }

    /// 
    /// out 协变 只能是返回结果
    /// 
    /// 
    public interface ICustomerListOut
    {
        T Get();
    }
    public class CustomerListOut : ICustomerListOut
    {
        public T Get()
        {
            return default(T);
        }
    }

    /// 
    /// in 逆变 只能是参数
    /// 
    /// 
    public interface ICustomerListIn
    {
        void Show(T t);
    }
    public class CustomerListIn : ICustomerListIn
    {
        public void Show(T t)
        { }
    }