欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

详细介绍C# 泛型

程序员文章站 2022-03-23 14:14:03
在c#开发中,必不可少的要用到泛型。泛型是.net2.0版本就有的,它广泛应用于c#框架中容器的使用中。下面我们来详细介绍一下。  一、泛型的主要优势    1.性能更高。    2.类型更安全。  ...

    在c#开发中,必不可少的要用到泛型。泛型是.net2.0版本就有的,它广泛应用于c#框架中容器的使用中。下面我们来详细介绍一下。

  一、泛型的主要优势

    1.性能更高。

    2.类型更安全。

    3.代码更多的重用和扩展性。

  二、泛型的基本使用

    泛型的一个主要优点是性能,我们来看下面的例子:

static void main(string[] args)
  {
   //不是泛型的集合类
   arraylist list = new arraylist();
   //添加一个值类型 装箱操作
   list.add(12);
   //去除第一个元素12 拆箱操作
   int num = (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:
  //  the system.collections.arraylist is read-only.-or- the system.collections.arraylist
  //  has a fixed size.
  public virtual int add(object value);

    相信大家都知道,装箱拆箱是比较损耗性能的,在执行add方法是, 把值类型转换成引用类型(装箱),取出来时在去拆箱,那怎么样才能避免这种情况发生呢?

再来看下面代码:

//泛型集合类
   list<int> list = new list<int>();
   list.add(12);
   int num = list[0];
   console.writeline(num);
   console.writeline("执行结束");
   console.readkey();

    这个时候,代码并没有发生装箱拆箱操作。

元数据中list类的add方法

//
  // 摘要:
  //  将对象添加到 system.collections.generic.list`1 的结尾处。
  //
  // 参数:
  // item:
  //  要添加到 system.collections.generic.list`1 末尾的对象。对于引用类型,该值可以为 null。
  public void add(t item);

    代码写到这里时,我们只是创建了一个list泛型集合,你可能还感觉不到泛型优势到底在哪里,你也可能不知道泛型到底是怎么用的。好,下面我们写个测试还有自己实际应用的例子。

测试arraylist和list集合的性能

static void main(string[] args)
  {
   //不是泛型的集合类
   arraylist arylist = new arraylist();
   //添加一个值类型 装箱操作

   //泛型集合类
   list<int> list = new list<int>();


   //计时类
   stopwatch watch = new stopwatch();
   watch.start();//开始计时
   for (int i = 0; i < 10000000; i++)
   {
    arylist.add(i);
   }
   watch.stop();
   console.writeline("arraylist用时:"+watch.elapsedmilliseconds);

   stopwatch watch1 = new stopwatch();
   watch1.start();//开始计时
   for (int i = 0; i < 10000000; i++)
   {
    list.add(i);

   }
   watch1.stop();
   console.writeline("list用时:" + watch1.elapsedmilliseconds);
   console.writeline("执行结束");
   console.readkey();
  }

    执行结果:

详细介绍C# 泛型

    以上的例子中,可以看出在计时的过程中代码写的重复了, 怎么解决这个问题呢, 泛型要排上用场了。

    我们想一下,相同的操作(都是循环添加元素,计算用时)用同一个方法实现不就ok了,只不过这个方法是泛型的(可以接受arraylist,也可以接受list),下面我们来写一下这个方法。  

//我们用t来代表泛型
  public static long gettime<t>(t t)
  {
   stopwatch watch = new stopwatch();
   watch.start();//开始计时
   for (int i = 0; i < 10000000; i++)
   {
    t.add(i);
   }
   watch.stop();
   return watch.elapsedmilliseconds;
  }

    但是问题来了, 这里并没有add方法让我们使用啊。 我们的思路是把这个t在调用时可以当作arraylist类型, 也可以当作list类型,这里就设计到了泛型约束。

  三、泛型约束

    如果使用泛型时, 想要调用这个泛型类型中的方法, 那么就需要添加约束。泛型约束主要有以下几种:

约束 说明
where t:struct 对于结构的约束, t必须是值类型
where t:class t必须是引用类型
where t:itest t必须实现了itest接口
where t:test t必须继承基类test
where t:new() t必须有默认构造函数
where t:t2 t派生自泛型类型t2,也称为裸类型约束

我们接着上个泛型方法来修改,arraylist和list都实现了接口ilist , 这个时候我们加上这个接口约束;

//我们用t来代表泛型
  public static long gettime<t>(t t)where t:ilist
  {
   stopwatch watch = new stopwatch();
   watch.start();//开始计时
   for (int i = 0; i < 10000000; i++)
   {
    t.add(i);
   }
   watch.stop();
   return watch.elapsedmilliseconds;
  }

调用结果:

详细介绍C# 泛型

    代码写到这里时,相信你已经对泛型有所了解了,但是真要应用到自己以后的逻辑编程中时,一定要善于总结:相同类型的相同方法,或者业务逻辑相同,只有某个判断不同时,可以用上泛型,不仅高效还代码量小。

  四、应用场景示范

    在我们的项目开发中,数据库的增删改查肯定是少不了的, 在这里我们用泛型来定义增删改查的泛型类。 之后建立一个用户表(实际应用中对应数据库中表结构),数据库中每一个表都可以用泛型类中定义的方法, 不需要每一个都写增删改查操作,也是面向对象编程的一种思想:

public class basedal<t>where t:class ,new ()
 {
  //以下是增删查改示范
  public void query(int id) {
   console.writeline(typeof(t).name+"查询方法,id="+id);
  }
  
  public void update(t t) {

   console.writeline(typeof(t).name+"更新方法");
  }
  public void delete(t t)
  {
   console.writeline(typeof(t).name + "删除方法");
  }

  public void add(t t) {
   console.writeline(typeof(t).name + "添加方法");
  }
 }
public class user
 {
  public int id { get; set; }
  public string name { get; set; }
 }

调用示范

basedal<user> dal = new basedal<user>();
   var user = new user()
   {
    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,写一个方法public static rectangle getrec() ;这个时候你会发现, 方法的泛型类型是抗变的, 就是我返回一个类型为rectangle但是我可以用shape来接收, 但泛型在net4.0之前不支持这个方式, 泛型在net4.0之后提供了支持泛型接口和泛型委托的协变和抗变。

普通方法抗变代码说明

//形状
 public class shape
 {
  public double width { get; set; }
  public double height { get; set; }

  public override string tostring()
  {
   return string.format("width:{0},height:{1}",width,height);
  }
 }

 //矩形
 public class rectangle : shape {

 }

///-----------------------------------方法与调用

public static rectangle getrec() {
   return new rectangle();
  }
 shape s = getrec();
   console.readkey();

    1、泛型接口的协变

      泛型接口在类型t前加上out关键字,这个时候泛型接口就是协变的,也就意味着返回类型只能是t。 直接看代码:

//泛型接口的协变
 public interface iindex<out t>
 {
  t gett(int index);
  int count { get; }
 }


 public class reccollection : iindex<rectangle>
 {
  private rectangle[] data = new rectangle[2] {
   new rectangle() { width=10,height=20 },
   new rectangle() {width=20,height=30 }
  };


  public int count
  {
   get
   {
    return data.count();
   }
  }

  public rectangle gett(int index)
  {
   return data[index];
  }
 }
//调用   
  shape s1 = new reccollection().gett(1);
   console.writeline(s1.tostring());

   iindex<rectangle> rec = new reccollection();
   iindex<shape> shapes = rec;
   for (int i = 0; i < shapes.count; i++)
   {
    console.writeline(shapes.gett(i));
   }
   console.readkey();

以上代码可以看出, 我们把泛型接口的泛型定义为矩形(rectangle), 但是在接受的时候可以用基类(shape) ,在这里实现了协变。

      2.泛型接口的抗变

        如果泛型类型用in关键字标注,那么这个泛型接口就是抗变的,这样,接口只能把泛型类型t用作其方法的输入。

//泛型接口的抗变
 public interface idisplay<in t>
 {
  void show(t item);
 }

 public class shapedisplay : idisplay<shape>
 {
  public void show(shape item)
  {
   console.writeline(item);
  }
 }
//-------------------------以下调用------
  rectangle recs = new rectangle() { width=100,height=200};
  idisplay<shape> shapedisplay = new shapedisplay();
  shapedisplay.show(recs);

  idisplay<rectangle> recdisplay = shapedisplay;
  recdisplay.show(recs);
  console.readkey();

以上代码可以看出泛型也是支持抗变和协变的。

    六、总结

      泛型是c#语言发展带来的新方法,以上例子只是简单的运用,希望大家能通过以上例子有所启发,能在项目中更好的使用泛型。以上还有泛型缓存没有说到,大家有兴趣可以找下资料,今天就到这里吧, 没有说到的还有不好的地方, 欢迎大家指正!

以上就是详细介绍c# 泛型的详细内容,更多关于c# 泛型的资料请关注其它相关文章!

相关标签: c# 泛型