详细介绍C# 泛型

    在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();
  }

    执行结果:

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

    我们想一下,相同的操作(都是循环添加元素,计算用时)用同一个方法实现不就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;
  }

调用结果:

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

  四、应用场景示范

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

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# 泛型的资料请关注我们其它相关文章!

(0)

相关推荐

  • C#泛型类创建与使用的方法

    本文实例为大家分享了C#泛型类创建与使用的具体代码,供大家参考,具体内容如下 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication13 { class Program { static void Main(string[] args) { Test<string,int> t = new Test<string,i

  • C#语法之泛型的多种应用

    本篇文章主要介绍泛型的应用. 泛型是.NET  work 2.0 版类库就已经提供的语法,主要用于提高代码的可重用性.类型安全性和效率. 泛型的定义 下面定义了一个普通类和一个泛型类,我们可以明确看到泛型类和普通类最大的区别就是多了一个<T>. 所以,这个<T>就标记了,这个类是泛型类.其中这个T,也可以写成A,B,C,D或其他字符. public class Generic { public String Name; } public class Generic<T>

  • C#实现泛型List分组输出元素的方法

    本文实例讲述了C#实现泛型List分组输出元素的方法.分享给大家供大家参考,具体如下: 背景:在输出列表时,往往需要按照某一字段进行分组,比如在输出城市列表时,按照首字母进行分组,输出学生列表时,按照年级进行分组,然后再对分组的结果按照其他的字段进行排序. 如存在以下STU学生类,代码如下: public class STU { public int ID { get; set; } public string Name { get; set; } public int Age { get; s

  • C#泛型类型知识讲解

    概述 泛型类和泛型方法兼具可重用性.类型安全性和效率,这是非泛型类和非泛型方法无法实现的 泛型通常与集合以及作用于集合的方法一起使用 泛型所属命名空间:System.Collections.Generic 可以创建自定义泛型接口.泛型类.泛型方法.泛型事件和泛型委托,以提供自己的通用解决方案,设计类型安全的高效模式 泛型允许编写一个可以与任何数据类型一起工作的类或方法 示例 using System; using System.Collections.Generic; namespace Gen

  • .NET/C#如何判断某个类是否是泛型类型或泛型接口的子类型详解

    前言 泛型:通过参数化类型来实现在同一份代码上操作多种数据类型.利用"参数化类型"将类型抽象化,从而实现灵活的复用.在.NET类库中处处都可以看到泛型的身影,尤其是数组和集合中,泛型的存在也大大提高了程序员的开发效率.更重要的是,C#的泛型比C++的模板使用更加安全,并且通过避免装箱和拆箱操作来达到性能提升的目的.因此,我们很有必要掌握并善用这个强大的语言特性. C#泛型特点: 1.如果实例化泛型类型的参数相同,那么JIT编辑器会重复使用该类型,因此C#的动态泛型能力避免了C++静态模

  • C#泛型概念的简介与泛型的使用

    C# 泛型(Generic) 定义:泛型允许我们延迟编写类或方法中的编程元素的数据类型的规范,直到实际在程序中使用它的时候.也就是说,泛型是可以与任何数据类型一起工作的类或方法. 泛型的使用:当我们的类/方法不需要关注调用者传递的实体是什么,这个时候就可以使用泛型. 泛型的特性: 使用泛型是一种增强程序功能的技术,具体表现在以下几个方面: 它有助于实现代码的重用.保护类型的安全以及提高性能. 我们可以创建泛型集合类.在 System.Collections.Generic 命名空间中包含了一些新

  • 实例讲解C# 泛型(Generic)

    泛型(Generic) 允许您延迟编写类或方法中的编程元素的数据类型的规范,直到实际在程序中使用它的时候.换句话说,泛型允许您编写一个可以与任何数据类型一起工作的类或方法. 您可以通过数据类型的替代参数编写类或方法的规范.当编译器遇到类的构造函数或方法的函数调用时,它会生成代码来处理指定的数据类型.下面这个简单的实例将有助于您理解这个概念: using System; using System.Collections.Generic; namespace GenericApplication {

  • 详解C# 泛型中的数据类型判定与转换

    提到类型转换,首先要明确C#中的数据类型,主要分为值类型和引用类型: 1.常用的值类型有:(struct) 整型家族:int,byte,char,short,long等等一系列 浮点家族:float,double,decimal 孤独的枚举:enum 孤独的布尔:bool 2.常用的引用类型有: string,class,array,delegate,interface 值得注意的是,无论是值类型还是引用类型,在C#中都派生于object,没错,这家伙就是万恶之源! 正是因为有了这一特性,于是我

  • 详细介绍C# 泛型

    在C#开发中,必不可少的要用到泛型.泛型是.NET2.0版本就有的,它广泛应用于C#框架中容器的使用中.下面我们来详细介绍一下. 一.泛型的主要优势 1.性能更高. 2.类型更安全. 3.代码更多的重用和扩展性. 二.泛型的基本使用 泛型的一个主要优点是性能,我们来看下面的例子: static void Main(string[] args) { //不是泛型的集合类 ArrayList list = new ArrayList(); //添加一个值类型 装箱操作 list.Add(12); /

  • fastjson 使用方法详细介绍

    Fastjson介绍 Fastjson是一个Java语言编写的JSON处理器. 1.遵循http://json.org标准,为其官方网站收录的参考实现之一. 2.功能qiang打,支持JDK的各种类型,包括基本的JavaBean.Collection.Map.Date.Enum.泛型. 3.无依赖,不需要例外额外的jar,能够直接跑在JDK上. 4.开源,使用Apache License 2.0协议开源.http://code.alibabatech.com/wiki/display/FastJ

  • 详细介绍Java函数式接口

    目录 Java-函数式接口 1.自定义函数式接口 1.1概述 1.2格式 1.3@FunctionalInterface注解 1.4自定义函数式接口 2.函数式编程 2.1Lambda的延迟执行 2.2使用Lambda作为参数和返回值 3.常用函数式接口 3.1Supplier接口 3.2Consumer接口 3.3Predicate接口 3.4Function接口 Java-函数式接口 1.自定义函数式接口 1.1概述 函数式接口在Java中是指:**有且仅有一个抽象方法的接口.**当然接口中

  • Java中ArrayList的使用详细介绍

    目录 1.ArrayList类 1.1ArrayList类概述 1.2ArrayList类常用方法 1.2.1构造方法 1.2.2成员方法 1.2.3示例代码 1.3ArrayList存储字符串并遍历 1.3.1案例需求 1.3.2代码实现 1.4ArrayList存储学生对象并遍历 1.4.1案例需求 1.4.2代码实现 1.5ArrayList存储学生对象并遍历升级版 1.5.1案例需求 1.5.2代码实现 总结 1.ArrayList类 1.1ArrayList类概述 在java中,我们会

  • Java详细分析讲解泛型

    目录 1.泛型概念 2.泛型的使用 2.1泛型类语法 2.2泛型方法语法 2.3泛型接口语法 2.4泛型在main方法中的使用 3.擦除机制 4.泛型的上界 5.通配符 5.1通配符的上界 5.2通配符的下界 6.包装类 6.1装箱和拆箱 1.泛型概念 泛型就是将类型参数化 所谓类型参数化就是将类型定义成参数的形式,然后在使用此类型的时候的时候再传入具体的类型 到这我们可以看出来:泛型在定义的时候是不知道具体类型的,需要在使用的时候传入具体的类型,泛型可以用在类.接口和方法中,这样做的好处是一个

  • Kotlin作用域函数应用详细介绍

    目录 1.前置知识 2.使用 3.源码赏析 3.1 let和run 3.2 also和apply 3.3 repeat 3.4 with 4.反编译 5.小结 平时看博客或者学知识,学到的东西比较零散,没有独立的知识模块概念,而且学了之后很容易忘.于是我建立了一个自己的笔记仓库 (一个我长期维护的笔记仓库,感兴趣的可以点个star~你的star是我写作的巨大大大大的动力),将平时学到的东西都归类然后放里面,需要的时候呢也方便复习. 1.前置知识 在Kotlin中,函数是一等公民,它也是有自己的类

  • Kotlin扩展函数超详细介绍

    目录 1.扩展函数 2.infix 关键字 3.扩展函数文件 4.重命名扩展函数 1.扩展函数 1)当我们没法接触某个类的定义,或者某个类没有用open修饰无法继承时,我们可以通过扩展函数,来实现该类功能的扩展.扩展函数,可以扩展自定义类.String类以及kotlin标准库中的其他类. 2)定义扩展函数和定义一般函数差不多.唯一的不同就是,定义扩展函数时,除了函数定义外,还需指定给哪个类进行扩展.如:fun String.addExtention() = "Kotlin: ".plu

  • Swift Extension扩展得使用详细介绍

    目录 扩展(Extension) 协议与初始化器 协议 泛型 扩展(Extension) 1.Swift中的扩展,有点类似于OC中的分类(Category) 2.扩展可以为枚举.结构体.类.协议添加新功能 可以添加方法.计算属性.下标.(便捷)初始化器.嵌套类型.协议等等 3.扩展不能办到的事情 不能覆盖原有的功能 不能添加存储属性,不能向已有的属性添加属性观察器 不能添加父类 不能添加指定初始化器,不能添加反初始化器 .... extension Double { var km: Double

  • Kotlin挂起函数的详细介绍

    Kotlin 协程的优势: 解决回调地狱的问题. 以同步的方式完成异步任务. 示例: fun main() { runBlocking { val a = getA() println(a) val b = getB(a) println(b) val c = getC(b) println(c) } } suspend fun getA(): String { withContext(Dispatchers.IO) { delay(2000L) } return "A content"

  • Kotlin扩展方法超详细介绍

    目录 前言 一.扩展方法 1.扩展方法的原型 2.扩展方法的使用 二.Kotlin扩展方法实现原理 三.泛型扩展方法 四.扩展属性 五.为伴生对象添加扩展 六.Kotlin 中常用的扩展 七.案例 前言 在这一节为大家继续带来 Kotlin 中的一些高级的内容:Kotlin 中的 Kotlin 扩 展(Extensions). Kotlin 能够扩展一个类的新功能而无需继承该类. 例如,你可以为一个你不 能修改的来自第三方库中的类编写一个新的函数. 这个新增的函数就像那个 原始类本来就有的函数一

随机推荐