【学习背景】 泛型,大家对这个词都不陌生了。很多时候,我们也在各个项目中经常见到和使用过。但对于它的一些理论知识,想必大家还是有所欠缺的,下面便是自己的补充学习过程。 【学习阶梯】 一.什么是泛型 当一个方法,需要被重复利用多次,但方法的参数类型并不相同,如果要想使用此方法,则需要复制此方法代码,加上不同的参数类型。为了解决这一问题,在方法中传入通用的参数类型,便是泛型。 二.为什么要使用泛型 下面以一个"栈"的代码实例来看:
public class Stack { private int[] m_item; public int Pop(){...} public void Push(int item){...} //参数类型为int型 public Stack(int i) //传入的参数类型必须为int型 { this.m_item = new int[i]; } }
上面的代码,栈中的参数类型为int型,如果现在需要将string类型的参数传进去,就需要把上面的方法复制一遍,将参数修改,改成push(string item).这样下去,只要参数类型不相同,就需要重复写一遍相同的代码。所以,为了提高代码的复用性,就需要对传递的参数进行抽象,自然想到了object类型。
三.使用object类型参数,修改以上代码
public class Stack { private int[] m_item; public int Pop(){...} public void Push(obj item){...} //参数类型为object类型 public Stack(int i) //传入的参数为int类型 { this.m_item = new int[i]; } }<span style="font-family:KaiTi_GB2312;font-size:18px;"> </span>
这样的方法很好的解决了参数类型不同的问题,但这样的方法并不是十全十美的。下面就先看看使用object类型作为参数的优缺点:
优点: 实现了代码的复用性,提高了代码的灵活性。 缺点: 当处理值类型数据时,使用此代码需要经过一个装箱和拆箱的操作。数据量大的时候,性能损失严重。 当处理引用类型时,不需要进行装箱操作,但需要将数据进行强制转换,增加了处理器的负担。 PS: 装箱:将值类型转换为引用类型的过程。 拆箱:将引用类型转换为值类型的过程。 四.使用泛型 下面是用泛型来重写上面的栈,用一个通用的数据类型T来作为一个占位符,等待在实例化时用一个实际的类型来代替。
public class Stack<T> { private T[] m_item; public T Pop(){...} public void Push(T item){...} //通用数据类型T作为参数类型 public Stack(int i) //传入int类型的参数 { this.m_item = new T[i]; } }
类的写法不变,只是引入了通用数据类型T就可以适用于任何数据类型,并且类型安全的。
下面分别传入int类型的参数和string类型的参数调用方法:
//实例化只能保存int类型的类 Stack<int> a = new Stack<int>(100); a.Push(10); a.Push("8888"); //这一行编译不通过,因为类a只接收int类型的数据 int x = a.Pop(); //实例化只能保存string类型的类 Stack<string> b = new Stack<string>(100); b.Push(10); //这一行编译不通过,因为类b只接收string类型的数据 b.Push("8888"); string y = b.Pop();<span style="font-family:KaiTi_GB2312;font-size:18px;"> </span>
五.使用泛型的好处
1.他是类型安全的。实例化了int类型的栈,就不能处理string类型的数据,其他数据类型也一样。 2.无需装箱和折箱。这个类在实例化时,按照所传入的数据类型生成本地代码,本地代码数据类型已确定,所以无需装箱和折箱。 3.无需类型转换。 六.泛型类中的约束 1.数据类型的约束 通用数据类型T就适合所有的数据类型吗?这个问题是值得思考的。答案是不一定的。所以,泛型中也需要对数据类型进行约束,约束的方式是指定T的祖先,即集成的接口或类。因为C#的单继承性,所以,约束可以有多个接口,但最多只能有一个类,并且类必须在接口之前,代码如下:
public class Node<T, V> where T : Stack, IComparable where V: Stack {...}<span style="font-family:KaiTi_GB2312;"> </span>
相信大家对此代码并不会感觉陌生,以上泛型类的约束表明,T是必须从Stack和IComparable集成,V必须是Stack或从Stack继承,否则将无法通过编译器的类型检查,编译失败。 2.实例化的约束 对T进行实例化,因为类中不知道类T有哪些构造函数,为了解决这一问题,需要用到new约束,代码如下:
public class Node<T, V> where T : Stack, new() where V: IComparable<span style="font-family:KaiTi_GB2312;"> </span>
3.关键字的约束 C#中数据类型有两大类:引用类型和值类型。在泛型的约束中,我们可以大范围地限制类型T必须是引用类型或必须是值类型,分别对应关键字class和struct。代码如下:
public class Node<T, V> where T : class where V: struct<span style="font-family:KaiTi_GB2312;font-size:18px;"> </span>
七.泛型方法 泛型不仅可以用在类中,也可以用在方法中,称为泛型方法。大家应该都还记得,我们在机房收费系统中,写过一个公共的方法,也就是将DataTable转换为list集合,那便是我们自己封装好的一个泛型方法。 【学习总结】 这一次的学习,主要是丰富了关于泛型的理论知识。对于泛型在ITOO项目中的应用,主要是出现在底层封装好的方法中。因为我们并不知道方法会传入怎么类型的参数,所以利用通用数据类型T表示,加上泛型各种约束,也就成了。最后,简言之,泛型可以提高性能、类型安全和质量,减少重复性的编程任务,简化总体编程模型。
时间: 2024-12-03 00:31:35