前言:前面两章介绍了C#的两个常用技术:C#基础系列——反射笔记 和 C#基础系列——Attribute特性使用 。这一章来总结下C#泛型技术的使用。据博主的使用经历,觉得泛型也是为了重用而生的,并且大部分时候会和反射一起使用。这次还是打算围绕WWH(即What、Why、How)来讲解。

1、什么是泛型:通过参数化类型来实现在同一份代码上操作多种数据类型。利用“参数化类型”将类型抽象化,从而实现灵活的复用。怎么理解呢,其实根据博主的理解,泛型就是将类型抽象化,使用抽象化的类型或对象去实现某些功能和业务,然后所有需要使用这些功能和业务的具体类型去调用泛型的方法和委托。呵呵,是不是还是有点晕,别着急,我们来个例子:

我们首先来定义一种场景:我们通过sql语句使用Ado.Net来查询默认得到的是弱类型的DataTable、DataReader等,而我们需要对查询到的结果集使用lamada表达式进行某些复杂的计算,需要将DataTable转换为对应的List<T>集合,首先来定义一个泛型的方法:

  1.     public static List<T> GetListByDateTable<T>(DataTable dt)
  2. {
  3. List<T> modelList = new List<T>();
  4. try
  5. {
  6. //1.如果DataTable没有数据则直接返回
  7. if (dt == null || dt.Rows.Count == 0)
  8. {
  9. return modelList;
  10. }
  11.  
  12. //2.遍历DataTable填充实体
  13. var lstCol = dt.Columns;
  14. foreach (DataRow dr in dt.Rows)
  15. {
  16. T model = default(T);
  17. //如果是object(这种一般用于一个实体类表示不了的情况),则先拼接json再反序列化为object
  18. if (typeof(T).Equals(typeof(object)))
  19. {
  20. var strJson = "{";
  21. foreach(DataColumn oCol in lstCol)
  22. {
  23. var oAttrValue = Convert.IsDBNull(dr[oCol.ColumnName]) ? null : dr[oCol.ColumnName];
  24. strJson += "\"" + oCol.ColumnName + "\":\"" + oAttrValue + "\",";
  25. }
  26. strJson = strJson.ToString().Trim(',') + "}";
  27. model = E2ERes.JavaScriptStrToObj<T>(strJson);
  28. }
  29. else
  30. {
  31. model = FillEntityByDT<T>(dt, dr);
  32. }
  33. modelList.Add(model);
  34. }
  35. }
  36. catch
  37. {
  38.  
  39. }
  40. return modelList;
  41. }
  42.  
  43. //通过DataTable填充实体类
  44. private static T FillEntityByDT<T>(DataTable dt, DataRow dr)
  45. {
  46. T model = (T)typeof(T).GetConstructor(new System.Type[] { }).Invoke(new object[] { });//反射得到泛型类的实体
  47. PropertyInfo[] pro = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public);
  48. Type type = model.GetType();
  49. foreach (PropertyInfo propertyInfo in pro)
  50. {
  51. if (dt.Columns.Contains(propertyInfo.Name))
  52. {
  53. if (Convert.IsDBNull(dr[propertyInfo.Name]))
  54. {
  55. continue;
  56. }
  57. if (!string.IsNullOrEmpty(Convert.ToString(dr[propertyInfo.Name])))
  58. {
  59. type.GetProperty(propertyInfo.Name).SetValue(model, dr[propertyInfo.Name], null);
  60. }
  61. }
  62. }
  63. return model;
  64. }

有了这个泛型的方法,我们在转换DataTable和具体的List<Model>的时候是不是就是一个很好的复用。

2、为什么要使用泛型:博主记得刚参加工作的前两年有一次面试的时候就被问到“泛型有什么优势?”,当时怎么回答的不记得了,只知道面试不太顺利~~为什么要用泛型呢?博主觉得泛型的主要优势有以下几点:

(1)保证了类型的安全性:泛型约束了变量的类型,保证了类型的安全性。例如List<int>和ArrayList。List<int>集合只能加入int类型的变量,ArrayList可以Add任何常用类型,编译的时候不会提示错误。

(2)避免了不必要的装箱、拆箱操作,提高程序的性能:泛型变量固定了类型,使用的时候就已经知道是值类型还是引用类型,避免了不必要的装箱、拆箱操作。举例说明:

使用泛型之前,我们使用object代替。

  1. object a=1;//由于是object类型,会自动进行装箱操作。
  2.  
  3. int b=(int)a;//强制转换,拆箱操作。这样一去一来,当次数多了以后会影响程序的运行效率。

使用泛型之后

  1. public static T GetValue<T>(T a)
  2.  
  3. {
  4.   return a;
  5. }
  6.  
  7. public static void Main()
  8.  
  9. {
  10.   int b=GetValue<int>(1);//使用这个方法的时候已经指定了类型是int,所以不会有装箱和拆箱的操作。
  11. }

(3)提高方法、算法的重用性。上面的例子基本能说明这个优势。

3、泛型的使用:

(1)泛型方法的使用:这也是博主使用最多的用法之一,像这种泛型方法一般是一些static的通用方法,例如原来项目中用到的一个将DataGridViewRow对象转换成对应的实体Model的方法如下:

  1.      public static T ToObject<T>(DataGridViewRow item) where T:class
  2. {
  3. var model = item.DataBoundItem as T;
  4. if (model != null)
  5. return model;
  6. var dr = item.DataBoundItem as System.Data.DataRowView;
  7. model = (T)typeof(T).GetConstructor(new System.Type[] { }).Invoke(new object[] { });//反射得到泛型类的实体
  8. PropertyInfo[] pro = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public);
  9. Type type = model.GetType();
  10. foreach (PropertyInfo propertyInfo in pro)
  11. {
  12. if (Convert.IsDBNull(dr[propertyInfo.Name]))
  13. {
  14. continue;
  15. }
  16. if (!string.IsNullOrEmpty(Convert.ToString(dr[propertyInfo.Name])))
  17. {
  18. var propertytype = propertyInfo.PropertyType;
  19. if (propertytype == typeof(System.Nullable<DateTime>) || propertytype == typeof(DateTime))
  20. {
  21. type.GetProperty(propertyInfo.Name).SetValue(model, Convert.ToDateTime(dr[propertyInfo.Name]), null);
  22. }
  23. else if (propertytype == typeof(System.Nullable<decimal>) || propertytype == typeof(decimal))
  24. {
  25. type.GetProperty(propertyInfo.Name).SetValue(model, Convert.ToDecimal(dr[propertyInfo.Name]), null);
  26. }
  27. else if (propertytype == typeof(System.Nullable<int>) || propertytype == typeof(int))
  28. {
  29. type.GetProperty(propertyInfo.Name).SetValue(model, Convert.ToInt32(dr[propertyInfo.Name]), null);
  30. }
  31. else if (propertytype == typeof(System.Nullable<double>) || propertytype == typeof(double))
  32. {
  33. type.GetProperty(propertyInfo.Name).SetValue(model, Convert.ToDouble(dr[propertyInfo.Name]), null);
  34. }
  35. else
  36. {
  37. type.GetProperty(propertyInfo.Name).SetValue(model, dr[propertyInfo.Name], null);
  38. }
  39. }
  40. }
  41. return model;
  42. }

使用泛型方法的注意事项:

  • 泛型方法的重载:public void Fun1<T>(T a);和public void Fun1<U>(U a);是无法重载的,这其实很好理解,因为T和U其实都是泛型的一个代表符号而已;
  • 泛型方法的重写:下面的方法重写FuncA的重写是正确的,FuncB的重写不正确,因为约束被默认继承,不用再写。
  1. abstract class BaseClass
  2. {
  3. public abstract T FuncA<T,U>(T t,U u) where U:T;
  4. public abstract T FuncB<T>(T t) where T:IComparable;
  5. }
  6.  
  7. class ClassA:BaseClass
  8. {
  9. public override X FuncA<X,Y>(X x,Y y){...}
  10. public override T FuncB<T>(T t) where T:IComparable{...}
  11. }

(2)泛型类的使用:

  1. public class Class_Base<DTO, T>
  2. {}

使用这个类的时候必须要指定两个泛型类。

(3)泛型接口以及泛型继承的使用:

泛型接口的类型参数要么已实例化,要么来源于实现类声明的类型参数

  1. public interface Interface_Base<T>
  2. {}
  3.  
  4. public class Class_Base<DTO, T> : Interface_Base<DTO>
  5. {}

DTO来源于实现类Class_Base

(4)泛型委托的使用:其实这种用法博主真的用得很少,只是原来见到过大牛们类似的代码。

定义泛型委托:

  1. public delegate void MyDelegate<T>(T obj);

泛型委托的使用:

  1. public delegate void MyDelegate<T>(T obj);
  2. static void Main(string[] args)
  3. {
  4. var method = new MyDelegate<int>(printInt);
  5. method(1);
  6. Console.ReadKey();
  7. }
  8. static void printInt(int i)
  9. {
  10. Console.WriteLine(i);
  11. }

(5)泛型约束:用来约束泛型类型有那些特性。常见的泛型约束也就那么几类:

泛型约束的格式

  1. public class Imps_Base<DTO, T> : Ifs_Base<DTO>
  2. where T : BaseEntity
  3. where DTO : class
  4. {
  5.   }
约束 说明

T:struct

类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型。

T:class

类型参数必须是引用类型,包括任何类、接口、委托或数组类型。

T:new()

类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。

T:<基类名>

类型参数必须是指定的基类或派生自指定的基类。

T:<接口名称>

类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。

T:U

为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。这称为裸类型约束.

转载自:http://www.cnblogs.com/landeanfen/p/4642820.html

【转载】C#基础系列——小话泛型的更多相关文章

  1. C#基础系列——小话泛型

    前言:前面两章介绍了C#的两个常用技术:C#基础系列——反射笔记 和 C#基础系列——Attribute特性使用 .这一章来总结下C#泛型技术的使用.据博主的使用经历,觉得泛型也是为了重用而生的,并且 ...

  2. C#基础系列——委托和设计模式(二)

    前言:前篇 C#基础系列——委托实现简单设计模式 简单介绍了下委托的定义及简单用法.这篇打算从设计模式的角度去解析下委托的使用.我们知道使用委托可以实现对象行为(方法)的动态绑定,从而提高设计的灵活性 ...

  3. C#基础系列——再也不用担心面试官问我“事件”了

    前言:作为.Net攻城狮,你面试过程中是否遇到过这样的问题呢:什么是事件?事件和委托的区别?既然事件作为一种特殊的委托,那么它的优势如何体现?诸如此类...你是否也曾经被问到过?你又是否都答出来了呢? ...

  4. C#基础系列——异步编程初探:async和await

    前言:前面有篇从应用层面上面介绍了下多线程的几种用法,有博友就说到了async, await等新语法.确实,没有异步的多线程是单调的.乏味的,async和await是出现在C#5.0之后,它的出现给了 ...

  5. C#基础系列——一场风花雪月的邂逅:接口和抽象类

    前言:最近一个认识的朋友准备转行做编程,看他自己边看视频边学习,挺有干劲的.那天他问我接口和抽象类这两个东西,他说,既然它们如此相像, 我用抽象类就能解决的问题,又整个接口出来干嘛,这不是误导初学者吗 ...

  6. c#基础系列(转)

    转:http://www.cnblogs.com/landeanfen/p/4953025.html C#基础系列——一场风花雪月的邂逅:接口和抽象类 前言:最近一个认识的朋友准备转行做编程,看他自己 ...

  7. .NET Core 小程序开发零基础系列(2)——小程序服务通知(模板消息)

    基于上一篇文件“.NET Core 小程序开发零基础系列(1)——开发者启用并校验牵手成功”的反映,个人觉得效果很不错,大家对公众号开发还是有很大需求的,同时也收到了很多同学的问题,后面我也会通过实战 ...

  8. 夯实Java基础系列13:深入理解Java中的泛型

    目录 泛型概述 一个栗子 特性 泛型的使用方式 泛型类 泛型接口 泛型通配符 泛型方法 泛型方法的基本用法 类中的泛型方法 泛型方法与可变参数 静态方法与泛型 泛型方法总结 泛型上下边界 泛型常见面试 ...

  9. [C# 基础知识梳理系列]专题六:泛型基础篇——为什么引入泛型

    引言: 前面专题主要介绍了C#1中的2个核心特性——委托和事件,然而在C# 2.0中又引入一个很重要的特性,它就是泛型,大家在平常的操作中肯定会经常碰到并使用它,如果你对于它的一些相关特性还不是很了解 ...

随机推荐

  1. eclipse软件与git配合使用创建git仓库

    一.在eclipse上安装git,和安装其他插件一样 help->Install new software->add... 在弹出框中输入name:git,location:http:// ...

  2. MySQL索引经验

    在数据库表中,使用索引可以大大提高查询速度. 假如我们创建了一个testIndex表:create TABLE testIndex(i_testID INT NOT NULL,vc_Name VARC ...

  3. μCOS-II系统之事件(event)的使用规则及Semaphore实例

    **************************************************************************************************** ...

  4. NEXYS 3开发板练手--USB UART(三)

    接着上一篇,今天我们来建立一个能用于实际工程中的DEMO. 首先,为了使我们的发送机不像上一个DEMO一样无节制的循环发送,我们需要修改代码,增加使发送机停止发送的控制部分,修改后的代码如下: `ti ...

  5. novas的verdi和debussy是干什么用的(关于debussy的一些介绍)

    source code window: 提供了一个比较友好的界面,将整个设计的source code按设计的层次结构以树状排布,并且可以在代码上反标仿真结果,支持查找.寻找驱动等一些debug常用的操 ...

  6. CCTableView(一)

    #ifndef __TABLEVIEWTESTSCENE_H__ #define __TABLEVIEWTESTSCENE_H__ #include "cocos2d.h" #in ...

  7. posix多线程--三种基本线程编程模型

    本文介绍了三种构建线程解决方案的方式. 一.流水线:每个线程执行同一种操作,并把操作结果传递给下一步骤的线程. 代码示例如下:终端输入一个int值,每个线程将该值加1,并将结果传给下一个线程. #in ...

  8. 孙鑫VC++视频教程笔记

    写在前面的话:在学习孙鑫老师的VC++视频时,为了加深自己对知识的深入理解,就做了下面的笔记. 第一讲: 第二讲: 第三讲: 第四讲: 第五讲: 第六讲: 第七讲: 第八讲: 第九讲: 第十讲: 第十 ...

  9. bss段和.data的是是非非

    一般情况下,一个程序本质上都是由 bss段.data段.text段三个组成的——本概念是当前的计算机程序设计中是很重要的一个基本概念. 而且在嵌入式系统的设计中也非常重要,牵涉到嵌入式系统运行时的内存 ...

  10. 【转】批量删除redis中的key

    1. DEL 直接加键名称 DEL key1 key2 key3 127.0.0.1:6379>  DEL site_msg_99973  false site_msg_99974   fals ...