详细介绍C# 泛型
在C#开发中,必不可少的要用到泛型。泛型是.NET2.0版本就有的,它广泛应用于C#框架中容器的使用中。下面我们来详细介绍一下。
一、泛型的主要优势
1.性能更高。
2.类型更安全。
3.代码更多的重用和扩展性。
二、泛型的基本使用
泛型的一个主要优点是性能,我们来看下面的例子:
staticvoidMain(string[]args) { //不是泛型的集合类 ArrayListlist=newArrayList(); //添加一个值类型装箱操作 list.Add(12); //去除第一个元素12拆箱操作 intnum=(int)list[0]; Console.WriteLine(num); Console.WriteLine("执行结束"); Console.ReadKey(); }
元数据中ArrayList类的Add方法
// //摘要: //将对象添加到System.Collections.ArrayList的结尾处。 // //参数: //value: //要添加到System.Collections.ArrayList末尾的System.Object。该值可以为null。 // //返回结果: //value已添加的System.Collections.ArrayList索引。 // //异常: //T:System.NotSupportedException: //TheSystem.Collections.ArrayListisread-only.-or-TheSystem.Collections.ArrayList //hasafixedsize. publicvirtualintAdd(objectvalue);
相信大家都知道,装箱拆箱是比较损耗性能的,在执行add方法是,把值类型转换成引用类型(装箱),取出来时在去拆箱,那怎么样才能避免这种情况发生呢?
再来看下面代码:
//泛型集合类 Listlist=newList (); list.Add(12); intnum=list[0]; Console.WriteLine(num); Console.WriteLine("执行结束"); Console.ReadKey();
这个时候,代码并没有发生装箱拆箱操作。
元数据中List类的Add方法
// //摘要: //将对象添加到System.Collections.Generic.List`1的结尾处。 // //参数: //item: //要添加到System.Collections.Generic.List`1末尾的对象。对于引用类型,该值可以为null。 publicvoidAdd(Titem);
代码写到这里时,我们只是创建了一个List泛型集合,你可能还感觉不到泛型优势到底在哪里,你也可能不知道泛型到底是怎么用的。好,下面我们写个测试还有自己实际应用的例子。
测试arraylist和list集合的性能
staticvoidMain(string[]args) { //不是泛型的集合类 ArrayListarylist=newArrayList(); //添加一个值类型装箱操作 //泛型集合类 Listlist=newList (); //计时类 Stopwatchwatch=newStopwatch(); watch.Start();//开始计时 for(inti=0;i<10000000;i++) { arylist.Add(i); } watch.Stop(); Console.WriteLine("Arraylist用时:"+watch.ElapsedMilliseconds); Stopwatchwatch1=newStopwatch(); watch1.Start();//开始计时 for(inti=0;i<10000000;i++) { list.Add(i); } watch1.Stop(); Console.WriteLine("list用时:"+watch1.ElapsedMilliseconds); Console.WriteLine("执行结束"); Console.ReadKey(); }
执行结果:
以上的例子中,可以看出在计时的过程中代码写的重复了,怎么解决这个问题呢,泛型要排上用场了。
我们想一下,相同的操作(都是循环添加元素,计算用时)用同一个方法实现不就ok了,只不过这个方法是泛型的(可以接受ArrayList,也可以接受List),下面我们来写一下这个方法。
//我们用T来代表泛型 publicstaticlongGetTime(Tt) { Stopwatchwatch=newStopwatch(); watch.Start();//开始计时 for(inti=0;i<10000000;i++) { t.Add(i); } watch.Stop(); returnwatch.ElapsedMilliseconds; }
但是问题来了,这里并没有Add方法让我们使用啊。我们的思路是把这个T在调用时可以当作ArrayList类型,也可以当作List类型,这里就设计到了泛型约束。
三、泛型约束
如果使用泛型时,想要调用这个泛型类型中的方法,那么就需要添加约束。泛型约束主要有以下几种:
约束 | 说明 |
whereT:struct | 对于结构的约束,T必须是值类型 |
whereT:class | T必须是引用类型 |
whereT:ITest | T必须实现了ITest接口 |
whereT:Test | T必须继承基类Test |
whereT:new() | T必须有默认构造函数 |
whereT:T2 | T派生自泛型类型T2,也称为裸类型约束 |
我们接着上个泛型方法来修改,ArrayList和List都实现了接口IList,这个时候我们加上这个接口约束;
//我们用T来代表泛型 publicstaticlongGetTime(Tt)whereT:IList { Stopwatchwatch=newStopwatch(); watch.Start();//开始计时 for(inti=0;i<10000000;i++) { t.Add(i); } watch.Stop(); returnwatch.ElapsedMilliseconds; }
调用结果:
代码写到这里时,相信你已经对泛型有所了解了,但是真要应用到自己以后的逻辑编程中时,一定要善于总结:相同类型的相同方法,或者业务逻辑相同,只有某个判断不同时,可以用上泛型,不仅高效还代码量小。
四、应用场景示范
在我们的项目开发中,数据库的增删改查肯定是少不了的,在这里我们用泛型来定义增删改查的泛型类。之后建立一个用户表(实际应用中对应数据库中表结构),数据库中每一个表都可以用泛型类中定义的方法,不需要每一个都写增删改查操作,也是面向对象编程的一种思想:
publicclassBaseDalwhereT:class,new() { //以下是增删查改示范 publicvoidQuery(intid){ Console.WriteLine(typeof(T).Name+"查询方法,id="+id); } publicvoidUpdate(Tt){ Console.WriteLine(typeof(T).Name+"更新方法"); } publicvoidDelete(Tt) { Console.WriteLine(typeof(T).Name+"删除方法"); } publicvoidAdd(Tt){ Console.WriteLine(typeof(T).Name+"添加方法"); } }
publicclassUser { publicintId{get;set;} publicstringName{get;set;} }
调用示范
BaseDaldal=newBaseDal (); varuser=newUser() { Id=0, Name="用户1" }; dal.Add(user); dal.Query(0); user.Name="用户11"; dal.Update(user); dal.Delete(user); Console.ReadKey();
五、泛型的协变和抗变
协变和抗变主要是对参数和返回值的类型进行转换,在.NET4之后可以通过协变和抗变为泛型接口或这泛型委托添加这个扩展。
现在我们写俩个类Shape(形状)、Rectangle(矩形类),Rectangle派生自Shape,写一个方法publicstaticRectangleGetRec();这个时候你会发现,方法的泛型类型是抗变的,就是我返回一个类型为Rectangle但是我可以用Shape来接收,但泛型在NET4.0之前不支持这个方式,泛型在NET4.0之后提供了支持泛型接口和泛型委托的协变和抗变。
普通方法抗变代码说明
//形状 publicclassShape { publicdoubleWidth{get;set;} publicdoubleHeight{get;set;} publicoverridestringToString() { returnstring.Format("width:{0},height:{1}",Width,Height); } } //矩形 publicclassRectangle:Shape{ } ///-----------------------------------方法与调用 publicstaticRectangleGetRec(){ returnnewRectangle(); } Shapes=GetRec(); Console.ReadKey();
1、泛型接口的协变
泛型接口在类型T前加上out关键字,这个时候泛型接口就是协变的,也就意味着返回类型只能是T。直接看代码:
//泛型接口的协变 publicinterfaceIIndex{ TGetT(intindex); intCount{get;} } publicclassRecCollection:IIndex { privateRectangle[]data=newRectangle[2]{ newRectangle(){Width=10,Height=20}, newRectangle(){Width=20,Height=30} }; publicintCount { get { returndata.Count(); } } publicRectangleGetT(intindex) { returndata[index]; } }
//调用 Shapes1=newRecCollection().GetT(1); Console.WriteLine(s1.ToString()); IIndexrec=newRecCollection(); IIndex shapes=rec; for(inti=0;i 以上代码可以看出,我们把泛型接口的泛型定义为矩形(Rectangle),但是在接受的时候可以用基类(Shape),在这里实现了协变。
2.泛型接口的抗变
如果泛型类型用in关键字标注,那么这个泛型接口就是抗变的,这样,接口只能把泛型类型T用作其方法的输入。
//泛型接口的抗变 publicinterfaceIDisplay{ voidShow(Titem); } publicclassShapeDisplay:IDisplay { publicvoidShow(Shapeitem) { Console.WriteLine(item); } } //-------------------------以下调用------ Rectanglerecs=newRectangle(){Width=100,Height=200}; IDisplay shapeDisplay=newShapeDisplay(); shapeDisplay.Show(recs); IDisplay recDisplay=shapeDisplay; recDisplay.Show(recs); Console.ReadKey(); 以上代码可以看出泛型也是支持抗变和协变的。
六、总结
泛型是C#语言发展带来的新方法,以上例子只是简单的运用,希望大家能通过以上例子有所启发,能在项目中更好的使用泛型。以上还有泛型缓存没有说到,大家有兴趣可以找下资料,今天就到这里吧,没有说到的还有不好的地方,欢迎大家指正!
以上就是详细介绍C#泛型的详细内容,更多关于C#泛型的资料请关注毛票票其它相关文章!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。