C#泛型基础知识点总结

2021-05-06 21:30

阅读:546

标签:c#   5.0   定义   time   鼠标   com   font   datetime   oid   

  1.0  什么是泛型

      泛型是C#2.0CLR(公共语言运行时)升级的一个新特性,泛型为.NET 框架引入了一个叫 type parameters(类型参数)的概念,type parameters 使得程序在设计的时候,不必设计其具体的参数,其具体的参数可以延迟到需要的时候声明或调用。使用泛型代码运行时避免了类型转换的装箱和拆箱操作。

  2.0 泛型的延迟声明

 1  1 using System;
 2  2 using System.Collections.Generic;
 3  3 using System.Linq;
 4  4 using System.Text;
 5  5 using System.Threading.Tasks;
 6  6 
 7  7 namespace _20171010Generic
 8  8 {
 9  9     /// 
10 10     /// 泛型方法相关类
11 11     /// 
12 12     public class GenericMethod
13 13     {
14 14         /// 
15 15         /// 泛型方法:方法带和type parameters(类型参数 T)的
16 16         /// 
17 17         /// 
18 18         /// 
19 19         public static void Show(T tParameters)
20 20         {
21 21             Console.WriteLine("{0}方法,parameter={1}参数,type={2}类型", typeof(GenericMethod).Name, tParameters, tParameters.GetType().Name);
22 22         }
23 23     }
24 24 }

如代码所示,在声明泛型方法的时候没有指定具体的参数类型,等到需要调用的时候再指定,这就叫做延迟声明。泛型的设计思想(延迟思想,推迟一切可以推迟的)

 1  1 using System;
 2  2 using System.Collections.Generic;
 3  3 using System.Linq;
 4  4 using System.Text;
 5  5 using System.Threading.Tasks;
 6  6 
 7  7 namespace _20171010Generic
 8  8 {
 9  9     class Program
10 10     {
11 11         static void Main(string[] args)
12 12         {
13 13 
14 14             int iValue = 123;
15 15             string sValue = "TestName";
16 16             DateTime dtValue = DateTime.Now;
17 17             object oValue = new object();
18 18 
19 19             GenericMethod.Show(iValue);
20 20             GenericMethod.Show(sValue);
21 21             GenericMethod.Show(oValue);
22 22             GenericMethod.Show(dtValue);
23 23             Console.WriteLine("———————我是华丽的分割线————————");
24 24             GenericMethod.Showint>(iValue);
25 25             GenericMethod.Showstring>(sValue);
26 26             GenericMethod.Showobject>(oValue);
27 27             GenericMethod.Show(dtValue);
28 28            
29 29             Console.WriteLine("———————我是华丽的分割线————————");
30 30             Console.WriteLine(typeof(Listint>));
31 31             Console.WriteLine(typeof(Dictionary));
32 32             Console.WriteLine("———————我是华丽的分割线————————");
33 33         }
34 34     }
35 35 }

技术分享

 泛型方法的调用,第一种  GenericMethod.Show(iValue);调用方法不指定类型参数,在编译的时候编译器自动编译推算(语法糖),第二种  GenericMethod.Show(iValue);调用方法指定类型参数,类型参数和参数类型须一致,否则编译不通过。VS2017鼠标移上去会提示可以简化方法名称。编译的时候,类型参数编译为占位符,程序运行的时候,JIT(即时编译(Just In-Time compile)即时编译为真实类型。所以使用泛型性能会比使用object作为参数的方法好,(ps:经过测试)。 Console.WriteLine(typeof(List)); 和Console.WriteLine(typeof(Dictionary));的运行结果中有个~1,和~2就表示类型参数的占位符。

  3.0 泛型主要的四种:泛型类, 泛型方法,泛型接口,泛型委托  

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _20171010Generic
{
    /// 
    /// 动物类
    /// 
    public class AnimalModel
    {
        public int Id { get; set; }
        public String Name { get; set; }
        public virtual void Cry()
        { }
    }
    public interface IEat
    {
        void Eat();
    }
    public interface ISleep
    {
        void Sleep();
    }

    /// 
    /// 狗类
    /// 
    public class Dog:AnimalModel
    {
        public override void Cry()
        {
            Console.WriteLine("旺旺旺。。。。。");
        }
    }
    /// 
    /// 猫类
    /// 
    public class Cat : AnimalModel
    {
        public override void Cry()
        {
            Console.WriteLine("喵喵瞄。。。。。。。");
        }
    }

    /// 
    /// 玫瑰花类
    /// 
    public class Rose
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

}

 首先先新建了一个AnimalModel类,里面定义了一个动物类,动物类里有个虚方法Cry,一个狗类,狗类继承了动物类,一个猫类,重写了虚方法Cry。一个IEat接口和ISleep接口,

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace _20171010Generic
 8 {
 9     /// 
10     /// 泛型类
11     /// 
12     /// 类型参数
13     /// 类型参数
14     /// 类型参数
15     public class GenericClass16     {
17         /// 
18         /// 无返回值的泛型方法
19         /// 
20         /// 
21         public void Show(T t)
22         {
23 
24         }
25         /// 
26         /// 有返回值的泛型方法
27         /// 
28         /// 
29         /// 
30         public T Get()
31         {
32             return default(T);
33         }
34     }
35 
36     /// 
37     /// 泛型接口
38     /// 
39     /// 
40     public interface ISleep41     {
42         W Sleep(W t);
43     }
44 
45     /// 
46     /// 有返回值的泛型委托
47     /// 
48     /// 
49     /// 
50     public delegate Y DlgYFun();
51 
52     public delegate int DlgIntFun();
53 
54     /// 
55     /// 泛型类
56     /// 
57     /// 
58     /// 
59     /// 
60     public class GenericChild61         //: GenericClass直接继承泛型类
62         //: GenericClass//类型参数可直接指定
63         //: ISleep
64         : ISleep//实现泛型接口
65     {
66         T ISleep.Sleep(T t)
67         {
68             return default(T);
69         }
70     }
71 
72     /// 
73     /// 普通类
74     /// 
75     public class Child
76        // :GenericClass错误的继承,普通类不能直接继承泛型类
77        //: GenericClass//必须指定全部确定的类型参数后可继承泛型
78        //:ISleep错误的实现泛型接口,普通类不能直接实现泛型接口,
79        : ISleepstring>
80     {
81         public string Sleep(string t)
82         {
83             Console.WriteLine("实现了sleep泛型接口,返回参数是:{0}", t);
84             return t;
85         }
86     }
87 }

泛型类就在普通类名字后面加上和多个类型参数,需要注意的是 1.普通类不能直接继承泛型类和泛型接口,因为泛型的类型参数不确定,但是泛型类或泛型接口指定类型后可以继承泛型类或实现泛型接口,2.泛型类可以直接继承泛型类,也可以直接实现泛型接口,其子类的类型参数相当于声明了局部参数。

  4.0泛型的约束(基类约束,接口约束,引用类型约束,值类型约束,无参构造函数约束)

    回到上面写的那个GenericMethod类里的show方法,new 一个cat对象,Cat cat=new Cat(){ Id=1,Name="小黑猫"}; 然后调用genericMentod.show(cat)方法。但是如果想要在show方法里访问Id,或者Name却不行。T是个不明确类型参数,所以无法访问,如图

技术分享

使用泛型约束解决方法:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace _20171010Generic
 8 {
 9     /// 
10     /// 泛型约束
11     /// 
12     public class Constraint
13     {
14        
15         public static void Show(T tParameter)
16             //where T: AnimalModel  基类约束,就可以访问该类的方法或属性
17             where T:Cat //或者该子类       
18         {
19             Console.WriteLine("泛型约束show方法--------id={0},name={1}",tParameter.Id,tParameter.Name);
20         }
21 
22         public static void Show(AnimalModel model)
23         {
24             Console.WriteLine("普通show方法--------id={0},name={1}", model.Id, model.Name);
25         }
26 
27         public static void ShowInterface(T tParameter)
28            //where T: AnimalModel  基类约束,就可以访问该类的方法或属性
29            where T : Cat,ISleep,IEat//或者该子类约束,多个接口约束 
30             
31         {
32             Console.WriteLine("泛型约束ShowInterface方法--------id={0},name={1}", tParameter.Id, tParameter.Name);
33             tParameter.Sleep();//接口的方法
34             tParameter.Eat();
35         }
36     }
37 
Constraint类里的第一个show方法中在后面带个 where关键字 约束类型,泛型方法里就能访问Id和Name,第二个show方法是作为对比,虽然第二个方法也能实现同样的效果,但是相对泛型方法不灵活,泛型方法可以同时约束多个,比如第三个方法约束多个接口,和类,多个约束的关系是&&关系
泛型约束除了基类约束和接口约束几种,还有值类型约束,无参构造约束,引用类型约束等这几种。
1  public static T TestFun()
2             // where T:class    //引用类型约束
3             // where T:struct   //值类型约束
4            where T : new()      //无参构造函数约束
5         {
6            T t = new T();
7             return default(T);
8         }

   5.0协变和逆变

 out 协变(covariant) 修饰返回值,in 逆变(contravariant) 修饰传入参数。out和in只能放在接口或者泛型委托的的参数前面,类没有协变和逆变。在.NET Framework里面,IEnumerable转到定义去看,其实就是个带out参数的泛型接口,Action转到定义去看就是个带in参数的泛型委托。还有一个逆变+协变的Func

技术分享

技术分享

像平常一样写代码:  AnimalModel animal = new AnimalModel();//实例化一个动物。

                   Dog dog = new Dog();//实例化一个条单身狗

          AnimalModel dog2 = new Dog();//实例化一条单身狗(狗继承了动物父类,父类出现的地方都可以用子类代替,对的,狗一定是个动物)

         // Dog dog3 = new AnimalModel();动物不一定是条单身狗,程序编译不通过

new一条单身狗没问题,new 一群单身狗试试看。

        List dogList = new List();//实例化一群单身狗(编译通过)

        List animalDog = new List();//实例化一群单身狗(语法上不通过)

理论上来说第二种实例化一群狗的方式是没毛病的,一群狗也一定是一群动物,但是程序上是不通过是因为Listt是个泛型  List不是继承List,没有父子关系,程序只认关系。。。

PS:写到这里我就快写不下去了,狗快被我自己玩坏了。

要使上面那句代码编译通过,可以通过lambda表达式转化  ListAnimalModel> animalDog = new List().Select(x => (AnimalModel)x).ToList();

使用IEnumerable:IEnumerable animalDog= new List();   //这就叫协变。IEnumerable在编译的时候就通过转化了,我个人理解为out 是表示转化后的T返回标识。平常在工作中,有用过out 关键字作为标识的返回参数,会用,但是不其所以然。原理明白后自己也可以定义一个协变的泛型接口。

 

 1  public interface IMyTestout T>
 2     {
 3 
 4     }
 5     public class Test : IMyTest 6     {
 7 
 8     }
 9 
10 
11    IMyTest test3 = new Test();

 

逆变就和协变相反。逆变的in 的参数只能作为传入值,不能作为返回值。说白了,也是一种约束。协变和逆变的关键作用就是让编译器在运行时不报错。

 1  1  public interface IMyTest 2  2     {
 3  3 
 4  4     }
 5  5     public class Test : IMyTest 6  6     {
 7  7 
 8  8     }
 9  9 
10 10 
11 11    IMyTest test3 = new Test();

 关于泛型的知识点还有很多,比如还有泛型的缓存,这个就有点难理解了。以上知识点是我平常通过各种途径学习总结的几点。如有不对欢迎指正。欢迎转载和分享,转载分享时请注明原创出处:如此拉风的女人

 

C#泛型基础知识点总结

标签:c#   5.0   定义   time   鼠标   com   font   datetime   oid   

原文地址:http://www.cnblogs.com/wwym/p/7646935.html


评论


亲,登录后才可以留言!