浅谈C#泛型
一.为什么要提出泛型的概念
我们在声明对象或者方法中,对象中成员变量的定义或者函数参数都传递都要指定具体的对象类型,但是有的时候参数的类型是变化的,但是实现的功能却又差不多,这个时候我们就想,是否存在一种东西可以将参数的位置“占住”,当传递具体的对象类型是再用这个类型取替换被占住的位置,这个时候就提出了泛型的概念,是不是有点绕,但是看完下面的例子就清除这里表达的内容了,泛型有多种表现形式,泛型类,泛型方法,泛型集合,泛型委托,可以说不懂泛型就没有真正的了解C#,下面让我们来开始泛型的学习吧。
二.泛型类,泛型方法
我们先举个例子,我们定义一个类来模拟入栈出栈操作,我们操作出栈入栈时要针对各种数据类型,int型,double 型,字符型......总之各种类型都有可能,我们不可能针对每个类型都写一个类来操作出栈入栈,这显然是不现实的,这个是时候就该泛型大显身手发时候了,看下面的定义:
public class MyStack<T> //多种类型时可以用T1,T2,T3......来表示 { public T[] objStack; public int _stackPoint; public int _stackSize; public MyStack(int Size) //成员变量一般在初始化的时候都要赋值 { objStack = new T[Size]; _stackSize = Size; _stackPoint = -1; } /// <summary> /// 入栈操作 /// </summary> /// <param name="item"></param> public void Push(T item) //这里要把T当成一种数据类型 { if (_stackPoint > _stackSize) { return; } else { _stackPoint++; objStack[_stackPoint] = item; } } /// <summary> /// 出栈操作 /// </summary> /// <returns></returns> public T Pop() { if (_stackPoint > 0) { _stackPoint--; return objStack[_stackPoint]; } else { return objStack[0]; } } }
我们在 public class MyStack<T> 后面加了一个<T>这个时候这个类就变成了一个泛型类,表示一个占位符,当我们实例化该类的时候需要传入具体的数据类型,我们来看一下泛型类的具体用法:
public int[] arrayInt = new int[6]; public string[] arrayStr = new string[6]; MyStack<int> objMyIntStack = new MyStack<int>(6); MyStack<string> objMyStrStack = new MyStack<string>(6);
这样泛型类就可以操作int 类型 和 string类型进行出栈入栈操作但代码却不需要改动。
三.泛型集合
使用泛型集合首先是是加了类型安全,方便编程,泛型集合指定了类型后只能将同类型的参数放入集合,泛型集合最常用就是List集合和Dictionary集合,我们分别看一下这两种集合。
A.Lis<T> 泛型集合
说到List泛型集合就不得不说ArrayList集合,ArrayList集合在操作是需要进行强制类型,极大的降低了代码处理效率所以,List集合应运而生,让我们看如下代码做个比较:
//用ArrayList集合来存储 ArrayList objArrayList = new ArrayList(); Students stu1 = new Students() { Name="小红",Age=20}; Students stu2 = new Students() { Name = "小明", Age = 30 }; objArrayList.Add(stu1); objArrayList.Add(stu2); Students obj = (Students)objArrayList[0]; Console.WriteLine(obj.Name + obj.Age.ToString()); //这里需要进行强制类型转换 Console.ReadLine(); //用List集合来存储 List<Students> objList = new List<Students>() { new Students(){Name = "小红",Age=20}, new Students(){Name="小明",Age = 30} }; foreach (var item in objList) { Console.WriteLine(item.Name + "," + item.Age.ToString()); } Console.ReadLine();
除此之外,我们一直在讲泛型集合可以保证数据安全,和ArrayList相比它的数据到底安全在什么地方呢,我们通过下面的例子做进一步说明:
ArrayList objArrayList = new ArrayList(); Students stu1 = new Students() { Name="小红",Age=20}; Students stu2 = new Students() { Name = "小明", Age = 30 }; Teacher tea1 = new Teacher() { Name = "小刚", Age = 30 }; objArrayList.Add(stu1); objArrayList.Add(stu2); objArrayList.Add(tea1); //Teacher类也可以添加进来,类型不安全 foreach (var item in objArrayList) { Students obj00 = (Students)item; }
从例子可以看出ArrayList集合的Add方法参数是object类型,所以Teacher的数据类型也可以放进去,这显然不是我们想要的,但是泛型集合就不一样,当占位符被确定的数据类型占用后,别的数据类型就添加不到集合中去。
List集合的常用方法,List集合中有很多方法,我们重点将一下Sort方法,Sort方法有四个重载方法,public void Sort();,public void Sort(Comparison<T> comparison);,
public void Sort(IComparer<T> comparer);,public void Sort(int index, int count, IComparer<T> comparer);我们直接调用Sort方法是按默认升序排序,假如某个类实现了IComparable接口那么默认排序就是按照接口中定义的方法来排序,看下面的例子:
//List集合排序 List<int> intList = new List<int>() { 1, 4, 3, 11, 8, 2, 0 }; intList.Sort(); foreach (var item in intList) { Console.WriteLine(item.ToString()); } Console.ReadLine();
输出结果为:0,1,2,3......结果为升序排序
//字符串排序 List<string> objListStr = new List<string>() { "c","a","b"}; objListStr.Sort(); foreach (var item in objListStr) { Console.WriteLine(item); } Console.ReadLine();
输出结果为:a,b,c
假如是对象类型呢,默认的排序方法为升序排序,但是对象之间没有升序的概念,这个时候该怎么办呢,看下面的代码:
public class Students : IComparable<Students> { public string Name { get; set; } public int Age { get; set; } /// <summary> /// 实现泛型接口 /// </summary> /// <param name="other"></param> /// <returns></returns> public int CompareTo(Students other) { return other.Name.CompareTo(this.Name); } } static void Main(string[] args) { Students stu1 = new Students() { Name = "Mick", Age = 20 }; Students stu2 = new Students() { Name = "Jack", Age = 30 }; List<Students> objList = new List<Students>(); objList.Add(stu1); objList.Add(stu2); objList.Sort(); foreach (var item in objList) { Console.WriteLine(item.Name); } Console.ReadLine(); }
Students类中实现了泛型接口IComparable<T> ,在泛型接口的方法中我们可以写排序的方式,这样做确实可以解决对象排序的问题,但是假如我们的排序条件是变化的,这种方式显然又不能满足我们的需求了,让我i们接着往下探索,如何实现集合对象的动态排序,让我们看如下代码:
/// <summary> /// 按姓名降序排列 /// </summary> public class NameDesc:IComparer<Students> { public int Compare(Students x, Students y) { return y.Name.CompareTo(x.Name); } } /// <summary> /// 按姓名升序排序 /// </summary> public class NameAsc : IComparer<Students> { public int Compare(Students x, Students y) { return x.Name.CompareTo(y.Name); } } /// <summary> /// 按年龄降序 /// </summary> public class AgeDesc:IComparer<Students> { public int Compare(Students x, Students y) { return y.Age - x.Age; } } /// <summary> /// 按年龄升序 /// </summary> public class AgeAsc : IComparer<Students> { public int Compare(Students x, Students y) { return x.Age.CompareTo(y.Age); } }
我们定义了一个自定义排序类,自定义排序类实现了ICompare接口。
static void Main(string[] args) { Students stu1 = new Students() { Name = "Mick", Age = 20 }; Students stu2 = new Students() { Name = "Jack", Age = 30 }; List<Students> objList = new List<Students>(); objList.Add(stu1); objList.Add(stu2); objList.Sort(new AgeDesc()); //基于接口实现多态的典型应用 foreach (var item in objList) { Console.WriteLine(item.Name); } Console.ReadLine(); }
调用List.Sort的重载方法,这里基于接口实现了多态,需要好好体会,关于集合的排序我们还可以用Linq查询。
B.Drictionary<> 泛型集合
List集合用索引查找元素的方法显然没有办法满足我们的实际需求,为了弥补这个缺陷,我们引入了字典的概念,说到键值对查询又不得不说说Hashtable,早期键值对集合都是用Hashtable类来实现的,后来泛型集合出现后Dictionary泛型集合取代了Hashtable类,让我们来看看两者的区别:
//用Hashtable集合 Hashtable objHashtable = new Hashtable(); objHashtable.Add("student1", new Students() { Name = "小王", Age = 20 }); objHashtable.Add("student2", new Students() { Name = "小李", Age = 25 }); Students stu =(Students)objHashtable["student1"]; //需要进行强制类型转换 //用Dictionary集合 Dictionary<string, Students> objDictionary = new Dictionary<string, Students>(); objDictionary.Add("student1", new Students() { Name = "小王", Age = 20 }); objDictionary.Add("student2", new Students() { Name="小李",Age = 25}); Students myStudent = objDictionary["student1"]; //不需要进行强制类型转换
从例子可以看出Hashtable集合操作都是object的类型,在进行对象操作是需要进行强制类型转换,但是Dictionary却不一样,不需要进行强制类型转换,所以可以这样讲Dictionary出现以后可以完全替Hashtable。
四.泛型委托
A.自定义泛型委托
static void Main(string[] args) { Mydelegate<int> objMydelegate = Add; Console.WriteLine("结果为:{0}", objMydelegate(1, 2)); Console.ReadLine(); } static int Add(int i1,int i2) { return i1 + i2; } } public delegate T Mydelegate<T>(T t1, T t2); //自定义泛型委托
以上例子就简单展示了自定泛型委托的使用方法,但是每次都这这么定义委托似乎很不方便,所以微软的工程师预先给我们定义好了几个泛型委托,我们可以直接使用,大大提高了使用泛型委托的便捷程度。
B.Func泛型委托的使用
Func是一个带返回值的泛型委托,func有多个重载版本,需要注意的是func最后一个参数是返回值类型,如果前面有泛型类型的参数,这个参数就是委托方法的形参类型,简单说func泛型委托就是一个带返回值的方法签名,我们先来看看它的简单应用:
static void Main(string[] args) { Func<int, int, int> objFunc = (a, b) => { return a + b; }; Console.WriteLine("结果为:{0}", objFunc(2, 5)); Console.ReadLine(); }
有人会说这样用似乎没什么意义,我们调用方法就可以直接实现功能,干嘛还要从委托转一下似乎多此一举,但是事实并不是如此,让我们看一下Func的复杂用法。现在提出一个需求,要求计算数组中任意指定开始位和结束位的“算数和” and 算数积。常规做法是:
static int GetSum(int[] nums, int from, int to) { int result = 0; for (int i = from; i <= to; i++) { result += nums[i]; } return result; } static int GetMulti(int[] nums, int from, int to) { int result = 1; for (int i = from; i <= to; i++) { result *= nums[i]; } return result; }
写两个方法,分别计算和与积,但是还有别的实现方法么,答案是肯定的:
static void Main(string[] args) { int[] nums = { 1, 2, 10, 4, 5, 6, 7, 8, 9 }; Console.WriteLine("数组前三个元素的和为:{0}", CommonMethod((a, b) => { return a + b; }, nums, 0, 3)); Console.WriteLine("数组前三个元素的积为:{0}", CommonMethod((a, b) => { return a * b; }, nums, 0, 3)); Console.ReadLine(); } static int CommonMethod(Func<int, int, int> com, int[] nums, int a, int b) { int result = nums[a]; for (int i = a + 1; i < b; i++) { result = com(result, nums[i]); } return result; }
其实这里也体现了委托的本质,委托本来就是为了把方法当成参数传递而设计的。
C.Action泛型委托
Action泛型委托和func泛型委托差不多,只不过Action是不带返回值的方法的签名,看下面的例子我们就可以了解Action泛型委托的用法:
static void Main(string[] args) { Action<string> objAction = (a) => { Console.WriteLine(a); }; objAction("Hello C#"); Console.ReadLine(); }
D.Predicate泛型委托
Predicate<T>委托定义如下:
public delegate bool Predicate<T>(T obj);
解释:此委托返回一个bool值的方法
在实际开发中,Predicate<T>委托变量引用一个“判断条件函数”,
在判断条件函数内部书写代码表明函数参数所引用的对象应该满足的条件,条件满足时返回true
看下面的例子:
static void Main(string[] args) { List<int> objList = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; List<int> resultList = objList.FindAll((s) => { return s > 2; }); //Predicate委托 foreach (var item in resultList) { Console.WriteLine(item); } Console.ReadLine(); }
好的以上就是关于泛型概念的总结,希望可以帮到有需要的人。