今天忙里偷闲在浏览外文的时候看到一篇讲C#中泛型的使用的文章,所以加上本人的理解以及四级没过的英语水平斗胆给大伙进行了翻译,固然在翻译的过程当中发现了一些问题,所以也进行了纠正,固然,原文的地址我放在最下面,若是你的英文水平比较好的话,能够直接直接阅读全文。同时最近建了一个.NET Core实战项目交流群637326624,有兴趣的朋友能够来相互交流。目前.NET Core实战项目之CMS的教程也已经更新了6篇了,目前两到三天更新一篇。html
做者:依乐祝
原文地址:http://www.javashuo.com/article/p-ytlwppbl-gq.html算法
C#和.NET中的泛型程序具备强类型集合的许多优势,并为代码提供更高质量和性能提高。泛型是C#语言和公共语言运行库(CLR)中的一个新功能,它将类型参数的概念引入.NET Framework。类型参数使得设计某些类和方法成为可能,例如,经过使用泛型类型参数T,能够大大简化类型之间的强制转换或装箱操做的过程(装箱、拆箱问题)。说白了,泛型就是经过参数化类型来实如今同一份代码上操做多种数据类型,利用“参数化类型”将类型抽象化,从而实现灵活的复用。每一个集合的详细规范能够在System.Collection.Generic名称空间下找到。编程
.Net定义了两种主要的数据类型来表示变量,也就是传说中的值类型和引用类型。这是须要装箱和拆箱的地方。装箱是一种经过将变量存储到System.Object中来显式地将值类型转换为引用类型的机制。当您装入值时,CLR会将新对象分配到堆中,并将值类型的值复制到该实例中。例如,您建立了一个int类型的变量:c#
int a = 20; object b = a; //装箱
相反的操做是拆箱,它是将引用类型转换回值类型的过程。此过程验证接收数据类型是否与装箱类型一致;数组
int c = (int)b; // 拆箱
C#编译器能够看到从int到object的赋值,反之亦然。当编译该程序并经过IL解析器检查IL生成的代码时,您会注意到当b被赋值为a时,程序经过在IL中自动插入一个box指令来响应,当c被赋值为b时以下;安全
代码加载常量20并将其存储在本地插槽中;它将值20加载到堆栈中并将其装箱。最后,它将被装箱的20返回到堆栈上,并将其拆箱为int类型数据结构
这个过程.NET CLR执行了一系列操做,例如,首先在托管堆中分配一个对象,而后在装箱中将值转换为内存位置,并在拆箱期间将值存储在堆上而且必须转回到堆栈。所以,从性能的角度来看,装箱和拆箱过程在泛型中具备很是重要的意义,由于这个过程若是不使用泛型的话会耗费更多地资源。ide
能够经过在类名后面加上
public class TestClass<T> { }
System.Collection.Generic命名空间下还定义了许多实现了这些关键字接口的类型。下表列出了此命名空间的核心类类型。性能
泛型类 | 描述 |
---|---|
Collection
|
泛型集合的基类,能够比较两个泛型对象是否相等 |
Dictionary<TKey, TValue> | 键值对的泛型集合 |
List
|
可动态调整列表项的大小 |
Queue
|
先进先出(FIFO)列表的泛型实现 |
Stack
|
后进先出(LIFO)列表的泛型实现 |
简单的泛型类示例
如下示例显示了一个简单的泛型类型的操做。TestClass
using System; using System.Collections.Generic; namespace GenericApp { public class TestClass<T> { // 定义一个长度为5的泛型类型的数组 T[] obj = new T[5]; int count = 0; // 向检讨类型添加数据 public void Add(T item) { //checking length if (count + 1 < 6) { obj[count] = item; } count++; } //foreach语句迭代索引 public T this[int index] { get { return obj[index]; } set { obj[index] = value; } } } class Program { static void Main(string[] args) { //用整形来实例化泛型类 TestClass<int> intObj = new TestClass<int>(); //向集合中添加int数据 intObj.Add(1); intObj.Add(2); intObj.Add(3); //没有装箱 intObj.Add(4); intObj.Add(5); //遍历显示数据 for (int i = 0; i < 5; i++) { Console.WriteLine(intObj[i]); //没有拆箱 } Console.ReadKey(); } } }
在构建并运行该程序以后,程序的输出以下所示;
泛型类型的一些重要特征使它们相比传统的非泛型类型具备以下的显著特征:
泛型最重要的特征之一是类型安全性。对于非泛型ArrayList类,若是使用对象类型,则能够向集合中添加任何类型,这些类型有时会致使严重的问题。下面的示例显示向ArrayList类型的集合添加一个整数、字符串和对象;
ArrayList obj = new ArrayList(); obj.Add(50); obj.Add("Dog"); obj.Add(new TestClass());
如今,若是使用整数对象来使用foreach语句进行遍历的话,当编译器接受到代码,可是由于集合中的全部元素都不是整数,因此会致使运行时异常;
foreach(int i in obj) { Console.WriteLine(i); }
编程的经验法则是应该尽早检测到错误。对于泛型类Test
Test<int> obj = new Test<int>(); obj.Add(50); obj.Add("Dog"); //编译错误 obj.Add(new TestClass()); //编译错误
在下面的示例中,ArrayList类存储对象,而且定义了Add()方法来存储一些整型参数。所以,整数类型被装箱。当使用foreach语句读取ArrayList中的值时,将发生拆箱。
ArrayList obj = new ArrayList(); obj.Add(50); //装箱- 值类型转换成引用类型 int x= (int)obj[0]; //拆箱 foreach(int i in obj) { Console.WriteLine(i); // 拆箱 }
注意:泛型比其余集合(如ArrayList)更快。
代替使用对象类型,TestClass
TestClass<int> obj = new TestClass<int>(); obj.Add(50); //没有装箱 int x= obj[0]; // 没有拆箱 foreach(int i in obj) { Console.WriteLine(i); //没有拆箱 }
泛型类型提供了一种源代码保护机制。泛型类能够定义一次,而且可使用许多不一样类型来进行实例化。泛型能够在一种CLR支持的语言中定义,并能够被另外一种.NET语言使用。如下TestClass
TestClass<int> obj = new TestClass<int>(); obj.Add(50); TestClass<string> obj1 = new TestClass<string>(); Obj1.Add("hello");
虽然大多数开发人员一般会使用基类库中的现有泛型类型,但也有可能会构建本身的泛型成员和自定义的泛型类型。
本示例的目的是构建一个交换方法,该方法可使用单个类型参数对任何可能的数据类型(基于值或基于引用)进行操做。因为交换算法的性质,传入的参数将做为使用ref关键字修饰的引用类型来进行发送。
using System; using System.Collections.Generic; namespace GenericApp { class Program { //泛型方法 static void Swap<T>(ref T a, ref T b) { T temp; temp = a; a = b; b = temp; } static void Main(string[] args) { //交换两个整形数据 int a = 40, b = 60; Console.WriteLine("Before swap: {0}, {1}", a, b); Swap<int>(ref a, ref b); Console.WriteLine("After swap: {0}, {1}", a, b); Console.ReadLine(); } } }
编译此泛型方法实现的程序后,输出以下所示;
字典也被称为映射或散列表。它表示容许您基于关键字来访问元素的数据结构。字典的一个重要特征是更快的查找; 您能够添加或删除选项而不会产生性能开销。
.Net提供了几个字典类,例如Dictionary <TKey,TValue>。类型参数TKey和TValue分别表示关键字的类型和它能够存储的值。
如下示例演示使用泛型的简单字典集合。在此程序中,将建立一个Dictionary类型对象,该对象接受int做为键,字符串做为值。而后咱们将一些字符串值添加到字典集合中,最后显示字典集合元素。
using System; using System.Collections.Generic; namespace GenericApp { public class Program { static void Main(string[] args) { //定义一个字典集合 Dictionary<int,string> dObj = new Dictionary<int,string>(5); //向字典中添加类型 dObj.Add(1,1,"Tom"); dObj.Add(2,"John"); dObj.Add(3, "Maria"); dObj.Add(4, "Max"); dObj.Add(5, "Ram"); //输出数据 for (int i = 1; i <= dObj.Count;i++) { Console.WriteLine(dObj[i]); } Console.ReadKey(); } } }
如下示例经过定义附加类emp来描述一些更复杂的问题,其中咱们覆盖ToString()方法以显示特定员工的名称和薪水。稍后在Main()方法中,建立一个新的Dictionary <TKey,TValue)的实例,其中键的类型为string,值为emp类型。构造函数分配2个元素的容量。emp对象和做为键的字符串值被添加到字典集合中。最后,使用foreach语句迭代集合元素并显示在屏幕上。
using System; using System.Text; using System.Collections.Generic; namespace GenericApp { public class emp { private string name; private int salary; public emp(string name,int salary) { this.name = name; this.salary = salary; } public override string ToString() { StringBuilder sb = new StringBuilder(200); sb.AppendFormat("{0},{1}",name,salary); return sb.ToString(); } } public class Program { static void Main(string[] args) { //定义一个字典集合 Dictionary<string, emp> dObj = new Dictionary<string, emp>(2); //向字典中添加元素 emp tom = new emp("tom", 2000); dObj.Add("tom",tom); // 键,值 emp john = new emp("john", 4000); dObj.Add("john",john); //print data foreach(Object str in dObj.Values) { Console.WriteLine(str); } Console.ReadKey(); } } }
队列是一种特殊类型的容器,可确保以FIFO(先进先出)方式访问元素。队列集合最适合实现消息传递的组件。咱们可使用如下语法定义Queue集合对象:
Queue qObj = new Queue();
Queue集合的属性,方法和其余规则定义都位于Sysyem.Collection命名空间下。下表定义了关键成员;
System.Collection.Queue成员 | 定义 |
---|---|
Enqueue() | 将对象添加到队列的末尾。 |
Dequeue() | 从队列的开头删除对象。 |
Peek() | 返回队列开头的对象而不删除它。 |
下面演示了一个基本的队列类型的集合,将一些字符串类型值添加到集合中,最后使用while语句来显示整个集合中的数据 。
using System; using System.Collections; namespace GenericApp { class Program { static void Main(string[] args) { //定义一个队列 Queue qObj = new Queue(); //向队列中添加字符串数据 qObj.Enqueue("Tom"); qObj.Enqueue("Harry"); qObj.Enqueue("Maria"); qObj.Enqueue("john"); //显示队列中的数据 while(qObj.Count !=0 ) { Console.WriteLine(qObj.Dequeue()); } Console.ReadKey(); } } }
Stack集合是LIFO的抽象(后进先出)。咱们可使用如下语法定义Stack集合对象:
Stack qObj = new Stack();
下表说明了堆栈的关键成员;
System.Collection.Stack成员 | 定义 |
---|---|
Contains() | 若是在集合中找到特定元素,则返回true。 |
Clear() | 删除集合的全部元素。 |
Peek() | 预览堆栈中的最新元素。 |
Push() | 它将元素推入堆栈。 |
Pop() | 返回并删除堆栈的顶部元素。 |
如下演示了堆栈集合。首先,将数组类型对象引用到堆栈集合中。而后使用Pop()方法从堆栈中删除集合中元素的值并显示在屏幕上。
using System; using System.Collections; namespace GenericApp { class Program { static void Main(string[] args) { int[] iArray = new int[] {1,2,3,4,5,6,7,8,9,10 }; //定义一个堆栈 Stack sObj = new Stack(iArray); Console.WriteLine("Total items="+sObj.Count); //显示集合数据 for (int i = 0; i < sObj.Count;++i ) { Console.WriteLine(sObj.Pop()); } Console.ReadKey(); } } }
在使用泛型实现的另外一个示例中,使用Push()方法将5个项添加到堆栈中。而后使用循环迭代输出堆栈中的数据。堆栈的枚举器不会删除数据; 它只是以LIFO方式返回每一个项目,以下所示:
using System; using System.Collections; namespace GenericApp { class Program { static void Main(string[] args) { //定义一个堆栈 Stack sObj = new Stack(); //向集合添加数据 for (int i = 0; i < 5; ++i) { sObj.Push(i+1); } Console.WriteLine("Total items=" + sObj.Count); //打印数据 foreach (int i in sObj) { Console.WriteLine(i); } Console.ReadKey(); } } }
今天忙里偷闲,在浏览外文的时候看到一篇讲泛型的文章,所以就加上本身的理解进行了相关翻译,也加深了本身对泛型的理解!若是英文比较好的话能够直接访问https://www.c-sharpcorner.com/UploadFile/84c85b/using-generics-with-C-Sharp/ 自行查看!固然,我在翻译的过程当中也发现了文中的一些错误,因此进行了更正!同时最近建了一个.NET Core实战项目交流群637326624,有兴趣的朋友能够来相互交流。目前.NET Core实战项目之CMS的教程也已经更新了6篇了,目前两到三天更新一篇。最后感谢你们的阅读。