你的位置:首页 > ASP.net教程

[ASP.net教程][.net 面向对象编程基础] 泛型


[.net 面向对象编程基础] (18) 泛型

    上一节我们说到了两种数据类型数组和集合,数组是指包含同一类型的多个元素,集合是指.net中提供数据存储和检索的专用类。

    数组使用前需要先指定大小,并且检索不方便。集合检索和声明方便,但是存在类型安全问题,本来使一个类型安全的C#变得不安全了。

    集合为了解决数组预设大小的问题,采取了一种自动扩容的办法,这样当大小不够时,他就创建一个新的存储区域,把原有集合的元素复制过来。如此又对性能上也是有很大的影响。

    上节我们说到解决这些缺陷的方法,那就是.NET 2.0以后,微软程序猿们推出来的新特性——泛型。

1.什么是泛型?

泛型是具有占位符(类型参数)的类、结构、接口和方法,这些占位符是类、结构、接口和方法所存储或使用的一个或多个占位符。

这个概念听起来比较绕,其实理解起来也不难,我的理解是类、接口、委托、结构或方法中有类型参数就是泛型类型,这样就有类型参数的概念。泛型集合类可以将类型参数用作它存储对象的点位符;类型参数作为其字段或方法的参数类型出现(这是MSDN中的描述)。

泛型集合所在的命我空间为:System.Collections.Generic

而List类是ArrayList的泛型等效类。该类使用大小按需动态增加的数组实现IList接口。使用方法就是IList<T>和List<T>,这个T就是你要指定的集合的数据或对象类型。

2.泛型声明

 泛型类: class Name<t>{}

 泛型方法: void Name(T t){}

 泛型接口:interface IName<T>{}

 泛型结构:struct Name<T>{}

 泛型委托:public delegate void Name<T>(T param);

3.泛型方法

    泛型我们在定义的时候,说明了他是可以使用占位符来占位类、结构、接口和方法的。我们先看一下方法使用泛型的例子。

我们还是使用前面的例子来看一下使用泛型:

类之间的关系UML图如下:

我们调用假如要实现,让每个动物都叫几声。该如何写呢?

 1 /// <summary> 2 /// 动物类(父类 抽象类) 3 /// </summary> 4 abstract class Animal 5 { 6   /// <summary> 7   /// 名字 8   /// 说明:类和子类可访问 9   /// </summary> 10   protected string name; 11  12   /// <summary> 13   /// 构造函数 14   /// </summary> 15   /// <param name="name"></param> 16   public Animal(string name) 17   { 18     this.name = name; 19   } 20  21   private int shoutNum = 8; 22   public int ShoutNum 23   { 24     get { return shoutNum; } 25     set { shoutNum = value; } 26   } 27  28   /// <summary> 29   /// 名字(虚属性) 30   /// </summary> 31   public virtual string MyName 32   { 33     get { return this.name; } 34   } 35  36   /// <summary> 37   /// 叫声,这个方法去掉虚方法,把循环写在这里 38   /// </summary> 39   public void Shout() 40   { 41     string result = ""; 42     for (int i = 0; i < ShoutNum; i++) 43       result += getShoutSound() + "!"; 44  45     Console.WriteLine(MyName); 46     Console.WriteLine(result); 47   } 48  49   /// <summary> 50   /// 创建一个叫声的虚方法,子类重写 51   /// </summary> 52   /// <returns></returns> 53   public virtual string getShoutSound() 54   { 55     return ""; 56   } 57  58   /// <summary> 59   /// 让所有动集合类的动物叫三次并报名字 (泛型) 60   /// </summary> 61   /// <param name="animal"></param> 62   public static void AnimalShout(IList<Animal> animal) 63   { 64     DateTime dt = System.DateTime.Now; 65     foreach (Animal anm in animal) 66     { 67       anm.Shout(); 68     } 69     Console.WriteLine("使用泛型让所有动物叫一遍所用时间为:" + (System.DateTime.Now - dt).TotalMilliseconds +"毫秒"); 70   } 71   /// <summary> 72   /// 让所有动集合类的动物叫三次并报名字 (重载方法 集合) 73   /// </summary> 74   /// <param name="animal"></param> 75   public static void AnimalShout(ArrayList animal) 76   { 77     DateTime dt = System.DateTime.Now; 78     foreach (Animal anm in animal) 79     { 80       anm.Shout(); 81     } 82     Console.WriteLine("使用集合让所有动物叫一遍所用时间为:" + (System.DateTime.Now - dt).TotalMilliseconds + "毫秒"); 83   } 84  85   /// <summary> 86   /// 让所有动集合类的动物叫三次并报名字 (重载方法 数组) 87   /// </summary> 88   /// <param name="animal"></param> 89   public static void AnimalShout(Animal[] animal) 90   { 91     DateTime dt = System.DateTime.Now; 92     foreach (Animal anm in animal) 93     { 94       anm.Shout(); 95     } 96     Console.WriteLine("使用数组让所有动物叫一遍所用时间为:" + (System.DateTime.Now - dt).TotalMilliseconds + "毫秒"); 97   } 98 } 99 100 101 /// <summary>102 /// 狗(子类)103 /// </summary>104 class Dog : Animal105 {106   string myName;107   public Dog(string name)108     : base(name)109   {110     myName = name;111   }112 113   /// <summary>114   /// 名字(重写父类属性)115   /// </summary>116   public override string MyName117   {118     get { return "我是:狗狗,我叫:" + this.name; }119   }120 121   /// <summary>122   /// 叫(重写父类方法)123   /// </summary>124   public override string getShoutSound()125   {126     return "汪!";127   }128 }129 130 /// <summary>131 /// 狗(子类)132 /// </summary>133 class ShepherdDog : Dog134 {135   string myName;136   public ShepherdDog(string name)137     : base(name)138   {139     myName = name;140   }141 142   /// <summary>143   /// 名字(重写父类属性)144   /// </summary>145   public override string MyName146   {147     get { return "我是:牧羊犬,我叫:" + this.name; }148   }149 150   /// <summary>151   /// 叫(重写父类方法)152   /// </summary>153   public override string getShoutSound()154   {155     return "汪~呜!";156   }157 }158 159 /// <summary>160 /// 猫(子类)161 /// </summary>162 class Cat : Animal163 {164   string myName;165   public Cat(string name)166     : base(name)167   {168     myName = name;169   }170   /// <summary>171   /// 名字(重写父类属性)172   /// </summary>173   public override string MyName174   {175     get { return "我是:猫咪,我叫:" + this.name; }176 177   }178 179   /// <summary>180   /// 叫(重写父类方法)181   /// </summary>182   public override string getShoutSound()183   {184     return "喵!";185   }186 }187 188 /// <summary>189 /// 猫(子类)190 /// </summary>191 class PersianCat : Cat192 {193   string myName;194   public PersianCat(string name)195     : base(name)196   {197     myName = name;198   }199   /// <summary>200   /// 名字(重写父类属性)201   /// </summary>202   public override string MyName203   {204     get { return "我是:波斯猫,我叫:" + this.name; }205 206   }207 208   /// <summary>209   /// 叫(重写父类方法)210   /// </summary>211   public override string getShoutSound()212   {213     return "喵~呜!";214   }215 }216 217 /// <summary>218 /// 羊(子类)219 /// </summary>220 class Sheep : Animal221 {222   string myName;223   public Sheep(string name)224     : base(name)225   {226     myName = name;227   }228   /// <summary>229   /// 名字(重写父类属性)230   /// </summary>231   public override string MyName232   {233     get { return "我是:羊羊,我叫:" + this.name; }234 235   }236   /// <summary>237   /// 叫(重写父类方法)238   /// </summary>239   public override string getShoutSound()240   {241     return "咩!";242   }243 }

调用方法:

 1 //数组 2 Animal[] animalArray = new Animal[] { new Dog("旺财"), new Cat("小花"), new Cat("阿狸"), new Sheep("纯羊"), new Dog("小白"), new ShepherdDog("汪羊"), new PersianCat("机猫") }; 3    4 //泛型      5 IList<Animal> animal = new List<Animal>();      6 animal.Add(new Dog("旺财")); 7 animal.Add(new Cat("小花")); 8 animal.Add(new Cat("阿狸")); 9 animal.Add(new Sheep("纯羊"));10 animal.Add(new Dog("小白"));11 animal.Add(new ShepherdDog("汪羊"));12 animal.Add(new PersianCat("机猫"));13 14 //集合15 ArrayList animalArrayList = new ArrayList();     16 animalArrayList.Add(new Dog("旺财"));17 animalArrayList.Add(new Cat("小花"));18 animalArrayList.Add(new Cat("阿狸"));19 animalArrayList.Add(new Sheep("纯羊"));20 animalArrayList.Add(new Dog("小白"));21 animalArrayList.Add(new ShepherdDog("汪羊"));22 animalArrayList.Add(new PersianCat("机猫"));23      24 25 //调用重载方法看它们的执行叫8次并报名字所需时间26 Animal.AnimalShout(animalArray);    27 Animal.AnimalShout(animal);       28 Animal.AnimalShout(animalArrayList);29 Console.ReadLine();

执行结果如下:

 以上的实例并没有模拟出能客观测试效率的环境,因为根据我们的经验数组并不能接受不同类型的元素,而集合和泛型可以,如果使用不同数据测试,也不是客观的。以上实例主要反映了泛型、数组、集合的使用方法,小伙伴们不要太纠结测试时间,不过泛型的时间确实是比较快的。有了泛型小伙伴们就不要再使用ArrayList这个不安全类型了。

数组、List和ArrayList的区别:

上节说了数组和集合ArrayList的区别,这节我们使用了泛型,再说一下他们三者的区别

数组:
(1)在内存中是连续存储的,所以它的索引速度是非常的快,而且赋值与修改元素也很简单。

(2)但是数组也存在一些不足的地方。比如在数组的两个数据间插入数据也是很麻烦的,还有我们在声明数组的时候,必须同时指明数组的长度,数组的长度过长,会造成内存浪费,数组和长度过短,会造成数据溢出的错误。这样如果在声明数组时我们并不清楚数组的长度,就变的很麻烦了.

集合ArrayList:

集合的出现就是为了解决数组的缺陷,但他本身也有缺陷,直到.NET 2.0以后出现泛型,我们可以说这是微软设计上的失误。
(1).ArrayList并非类型安全

ArrayList不论什么类型都接受,实际是接受一个object类型。

比如如下操作:

ArrayList ar = new ArrayList();

ar.Add(111);

ar.Add("bbb");

我们使用foreach遍历的时候   foreach(int array in ar){}那么遇到”bbb”则程度报错,因此我们说他是非安全类型。

(2).遍历ArrayList资源消耗大

因此类型的非安全,我们在使用ArrayList的时候,就意味着增加一个元素,就需要值类型转换为Object对象。遍历的时候,又需要将Object转为值类型。

就是装箱(boxing,指将值类型转换为引用类型)   和
拆箱(unboxing,指将引用类型转换为值类型)

由于装箱了拆箱频繁进行,需要大量计算,因此开销很大。

泛型List:

List和AraayList都是继承于IList,属于等效类,他们之间的区别也是对集合ArrayList局限性的修改

(1)类型安全,通过允许指定泛型类或方法操作的特定类型,泛型功能将类型安全的任务从您转移给了编译器。不需要编写代码来检测数据类型是否正确,因为会在编译时强制使用正确的数据类型。减少了类型强制转换的需要和运行时错误的可能性。

(2)减少开销,泛型提供了类型安全但没有增加多个实现的开销。

3.泛型类

对于泛型类,我们先解决一下实际例子:假如我们有一个泛型类,不知道是什么类型,在初始化的时候再指定类型。类里面有一个方法,可以接受初始化的参数类型,也就是一个泛型方法。

/// <summary>/// 泛型有一个泛型方法,该方法有一个泛型参数/// </summary>/// <typeparam name="T"></typeparam>class MyClass<T>{  public static T F(T param)  {    return param;  }}

调用:

//泛型类调用int param = 2, param2= 3;string result1 = (MyClass<string>.F(param.ToString()) + param2).ToString();string result2 = (MyClass<int>.F(param) + param2).ToString();Console.WriteLine(result1);Console.WriteLine(result2);Console.ReadLine();

可以看到,输出结果为: 23 和 5 ,使用泛型,我们不会因为数据类型不同,就需要去复制一个方法去处理了。

4.类型约束

我们上面定义了泛型,他们默认没有类型约束,就是说可以是任意类型,比如某些情况下,我们需要约束类型,比如,不允许使用 int型

我们使用where子句来约束泛型的类型

主要有以下六种类型的约束:

约束

说明

T:结构

类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型。有关更多信息,请参见使用可空类型(C# 编程指南)。

T:类

类型参数必须是引用类型,包括任何类、接口、委托或数组类型。

T:new()

类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。

T:<基类名>

类型参数必须是指定的基类或派生自指定的基类。

T:<接口名称>

类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。

T:U

为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。这称为裸类型约束。

关于泛型约束的实例,晚上继续写,回家次饭了。

 

 

============================================================================================== 

返回目录

 <如果对你有帮助,记得点一下推荐哦,有不明白的地方或写的不对的地方,请多交流>
 

==============================================================================================