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

[.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 : Animal
105 {
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 MyName
117     {
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 : Dog
134 {
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 MyName
146     {
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 : Animal
163 {
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 MyName
174     {
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 : Cat
192 {
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 MyName
203     {
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 : Animal
221 {
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 MyName
232     {
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 提供的参数。这称为裸类型约束。

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

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

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

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

时间: 2024-10-06 13:43:39

[.net 面向对象编程基础] (18) 泛型的相关文章

[.net 面向对象编程基础] (20) LINQ使用

[.net 面向对象编程基础] (20)  LINQ使用 通过上节LINQ的基础知识的学习,我们可以开始使用LINQ来进行内存数据的查询了,我们上节说了LINQ的定义为:Language Integrated Query(语言集成查询)的简称,它是集成在.NET编程语言中的一种特性. 1.LINQ的构架 从这幅图中,我们可以知道LINQ包括五个部分:LINQ to Objects.LINQ to XML.LINQ to SQL.LINQ to DataSet.LINQ to Entities.

[.net 面向对象编程基础] (17) 数组与集合

[.net 面向对象编程基础] (17) 数组与集合 学习了前面的C#三大特性,及接口,抽象类这些相对抽象的东西以后,是不是有点很累的感觉.具体的东西总是容易理解,因此我们在介绍前面抽象概念的时候,总是举的是具体的实例以加深理解. 本节内容相当具体,学起来也相当轻松. 1.数组 1.1 什么是数组? 数组是一种数据结构,包含同一个类型的多个元素. 1.2数组的初始化 string[] mystringArray; 类型+方框号 数组名 1.3数组初始化 我们知道数组是引用类型,所以需要给他分配堆

[.net 面向对象编程基础] (22) 事件

[.net 面向对象编程基础] (22)  事件 事件(Event)是学习.net面向对象编程很重要的一部分,在学习事件之前,我们实际上已经在很多地方使用了事件,比如控件的click事件等,这些都是.net设计控件的时候已经定义好的事件.除此之外,我们同样可以自己定义事件. 事件实际上是一种消息机制,当然点击控件时,click就通知处理他的方法去处理,实际上就是前面说的委托.因此我们可以说:事件是一种具有特殊签名的委托.而事件/消息机制是windows的核心,因此我们必须掌握他. 为了更加容易理

[.net 面向对象编程基础] (14) 重构

[.net 面向对象编程基础] (14) 重构 通过面向对象三大特性:封装.继承.多态的学习,可以说我们已经掌握了面向对象的核心.接下来的学习就是如何让我们的代码更优雅.更高效.更易读.更易维护.当然了,这也是从一个普通程序员到一个高级程序员的必由之路.就看病一样,普通医生只能治标,高级医生不但看好病,还能除病根. 1.什么时重构? 重构(Refactoring)就是在不改变软件现有功能的基础上,通过调整程序代码改善软件的质量.性能,使其程序的设计模式和架构更趋合理,提高软件的扩展性和维护性.

[.net 面向对象编程基础] (21) 委托

[.net 面向对象编程基础] (20)  委托 上节在讲到LINQ的匿名方法中说到了委托,不过比较简单,没了解清楚没关系,这节中会详细说明委托. 1.什么是委托? 学习委托,我想说,学会了就感觉简单的不能再简单了,没学过或都不愿了解的人,看着就头大,其实很简单.委托在.net面向对象编程和学习设计模式中非常重要,是学习.net面向对象编程必须要学会并掌握的. 委托从字面上理解,就是把做一些事情交给别人来帮忙完成.在C#中也可以这样理解,委托就是动态调用方法.这样说明,就很好理解了. 平时我们会

[.net 面向对象编程基础] (16) 接口

[.net 面向对象编程基础] (16) 接口 关于“接口”一词,跟我们平常看到的电脑的硬件“接口”意义上是差不多的.拿一台电脑来说,我们从外面,可以看到他的USB接口,COM接口等,那么这些接口的目的一就是让第三方厂商生产的外设都有相同的标准,也是提供一个对外通信或操作的入口. 只是C#的接口除了以上特点之外,还具有一种类似于模板的功能,我们定义一组接口,就像是一个模板.这点和抽象类不同,抽象类是先有子类或都子类的概念,从中抽象出一个类.而接口更像是我们要设计一台机器,先把这台机器对外的功能接

[.net 面向对象编程基础] (15) 抽象类

[.net 面向对象编程基础] (15) 抽象类 前面我们已经使用到了虚方法(使用 Virtual修饰符)和抽象类及抽象方法(使用abstract修饰符)我们在多态一节中说到要实现类成员的重写必须定义为一个虚方法或抽象方法.这节单独把抽象类提出来,是因为抽象是.net实现面向对象编程重要的重要思想,定义抽象类就象一个模板一个提纲,它可以理解为中央的指导思想,我们通过派生类去具体实现它.由此可见,抽象类本身没有任何作用,也不能被实例化,因为他本身就不具有具体的事物.比如上节的动物类的例 子,我们实

[.net 面向对象编程基础] (11) 面向对象三大特性——封装

[.net 面向对象编程基础] (11) 面向对象三大特性——封装 我们的课题是面向对象编程,前面主要介绍了面向对象的基础知识,而从这里开始才是面向对象的核心部分,即 面向对象的三大特性:封装.继承.多态. 1.封装概念 封装:每个对象都包含有它能进行操作的所有信息,这个特性称为封装.这样的方法包含在类中,通过类的实例来实现. 2.封装的优点 A.良好的封装能够减少耦合(比如实现界面和逻辑分离) B.可以让类对外接口不变,内部可以实现自由的修改 C.类具有清晰的对外接口,使用者只需调用,无需关心

[.net 面向对象编程基础] (7) 基础中的基础——流程控制语句

[.net 面向对象编程基础] (7) 基础中的基础——流程控制语句 本来没有这一节的内容,后来考虑到既然是一个系列文章,那么就尽可能写的详细一些,本节参考了网上朋友所写的例子,为的是让更多小伙伴学习,提高,加薪,如有版权问题,请邮件我,我第一时间处理. 语句:是程序中的小指令,本节主要以流程控制语句为主要内容. 流程控制语句中最常用的三个是 选择语句(即条件语句).循环语句和异常处理语句 流程控制语句分类: 类别    关键字 选择语句  if.else.switch.case 循环语句  d