关键词:
在上一篇文章中,介绍了什么是泛型,以及泛型和非泛型的区别,这篇文章主要讲一些泛型的高级用法,泛型方法,泛型泛型接口和泛型委托,协变和逆变泛型类型参数和约束性,泛型的高级用法在平时的业务中用的不多,多用于封装高级方法和一些底层封装,前几天读了一篇文章,如何选择网络上的技术文章,因为现在关于技术的文章可以说非常多,但是时间是有限的,如果花很多时间阅读了一篇文章却没有什么用,岂不是很浪费时间,所以第一步选择自己感兴趣的文章阅读,第二要把阅读过的文章尽可能实现一次,读书万遍不如走上一遍,第三尽量不读翻译性的文章,这里其实我觉得不是所有人都能很轻松的看懂官方文档,所以这点还是仁者见仁,智者见智。为了让文章尽可能的有深度,所以我觉得以后的博文中应该尽可能的贴出的知识模块都有所解释,有所理解。不是在网上复制粘贴以后在给别人看,博文不是笔记,所以要说出自己的见解
说了这么多,那么就开始务实的甩开膀子做吧!
泛型方法
既然讲到了泛型是为了高级封装,那么我们就来封装一个C#中的ORM吧,在封装ORM之前还要有一个SQL帮助类,这个网上有很多,感兴趣的可以直接到网上找一个,C#中封装的ORM最好的Entity FromWork,感兴趣的可以看下源码,我们先看下下面的代码,这是一个典型的泛型方法
1 /// <summary> 2 /// 查询获得一个实体 3 /// </summary> 4 /// <typeparam name="T"></typeparam> 5 /// <param name="sql"></param> 6 /// <param name="sqlParameters"></param> 7 /// <param name="transaction"></param> 8 /// <returns></returns> 9 public static T Get<T>(string sql, IList<SqlParameter> sqlParameters = null, IDbTransaction transaction = null) 10 { 11 DataTable table = SQLServerHelper.Query(sql, sqlParameters, transaction); 12 if (table != null && table.Rows != null && table.Rows.Count > 0) 13 { 14 DataRow row = table.Rows[0]; 15 return ConvertRowToModel<T>(row, table.Columns); 16 } 17 else 18 { 19 Type modeType = typeof(T); 20 return default(T); 21 } 22 23 }
注释已经表明了这是用来获取一个实体的Get<T>()方法中T定义为泛型类型,方法有一个必选参数两个默认参数,string类型的sql语句,默认IList<SqlParameter> sqlParameters继承自IList集合的SQL参数。这是参数化SQL的,每一个SQL语句都应该写成参数化的,还有一个IDbTransaction transaction = null是否开启事务,有了这样三个参数就可以定义一个查询指定SQL语句,是否有Where条件,是否开启事务的方法,方法继续执行,第是一行代码是使用帮助类获得数据,第14行和第15行是将获得数据映射成对应的结构体,我们可以看下 ConvertRowToModel<T>(row, table.Columns)的内部实现
1 public static T ConvertRowToModel<T>(DataRow row, DataColumnCollection columns) 2 { 3 Type modeType = typeof(T); 4 Object model = Activator.CreateInstance(modeType); 5 6 foreach (var p in model.GetType().GetProperties()) 7 { 8 var propertyName = p.Name.ToUpper(); 9 var propertyType = p.PropertyType.Name; 10 if (columns.Contains(propertyName)) 11 { 12 var value = row[propertyName]; 13 14 if (propertyType.ToUpper().Contains("STRING")) 15 { 16 17 if (Convert.IsDBNull(value)) 18 { 19 value = string.Empty; 20 } 21 else 22 { 23 p.SetValue(model, value.ToString(), null); 24 } 25 } 26 else if (propertyType.ToUpper().Contains("INT")) 27 { 28 29 if (Convert.IsDBNull(value)) 30 { 31 value = 0; 32 } 33 34 p.SetValue(model, Int32.Parse(value.ToString()), null); 35 } 36 else if (propertyType.ToUpper().Contains("SINGLE")) 37 { 38 39 if (Convert.IsDBNull(value)) 40 { 41 value = 0.0f; 42 } 43 p.SetValue(model, Single.Parse(value.ToString()), null); 44 } 45 else if (propertyType.ToUpper().Contains("DATETIME")) 46 { 47 48 if (Convert.IsDBNull(value)) 49 { 50 value = DateTime.MinValue; 51 } 52 p.SetValue(model, DateTime.Parse(value.ToString()), null); 53 } 54 else if (propertyType.ToUpper().Contains("DOUBLE")) 55 { 56 57 if (Convert.IsDBNull(value)) 58 { 59 value = 0.0d; 60 } 61 p.SetValue(model, Double.Parse(value.ToString()), null); 62 } 63 else if (propertyType.ToUpper().Contains("BOOLEAN")) 64 { 65 66 if (Convert.IsDBNull(value)) 67 { 68 value = false; 69 } 70 if (value.GetType() == typeof(Int32)) 71 { 72 p.SetValue(model, Int32.Parse(value.ToString()) == 1, null); 73 74 } 75 else if (value.GetType() == typeof(String)) 76 { 77 p.SetValue(model, Boolean.Parse(value.ToString()), null); 78 } 79 else if (value.GetType() == typeof(Boolean)) 80 { 81 p.SetValue(model, (Boolean)(value), null); 82 } 83 84 } 85 else if (p.PropertyType.IsEnum)//Enum 86 { 87 if (Convert.IsDBNull(value) || string.IsNullOrEmpty(value.ToString())) 88 { 89 value = "0"; 90 } 91 92 p.SetValue(model, int.Parse(value.ToString()), null); 93 } 94 else if (propertyType.ToUpper().Contains("DECIMAL")) 95 { 96 97 if (Convert.IsDBNull(value)) 98 { 99 value = 0.0f; 100 } 101 p.SetValue(model, Decimal.Parse(value.ToString()), null); 102 } 103 104 } 105 } 106 return (T)model; 107 }
这个方法有点长,但实际上很好理解,并且这个方法也是一个泛型方法,其中的关键点在于他会把所有的字段类型转换成基础的元类型,转换成int,string这些最终会在返回给Get<T>()方法,这样就完成了实体的映射,那么有了上面两个方法,就可以编写简单的ORM了,如果是增删查改的话,就改变其中的逻辑,获得返回的影响行数就可以了,那么如何调用这个ORM呢,封装后最主要的是使用。可以用如下方法直接调用
1 public static Student Getmodel(long id) { 2 StringBuilder sql = new StringBuilder(); 3 List<SqlParameter> args = new List<SqlParameter>(); 4 //根据学生id获取学生信息 5 sql.Append("select * from student where [email protected]"); 6 //id参数化后赋值 7 args.Add(new SqlParameter("@id", id)); 8 //调用封装ORM所在的CommonDao类 调用刚才的Get方法 9 //映射类Student,就会返回和Student类相符合的数据库字段内容 10 return CommonDao.Get<Student>(sql.ToString(), args); 11 }
是不是就简单了很多呢。当然你也可以封装的更深一些,这里还是在传递sql语句,如果你喜欢Entitie fromwork那种方式,可以把Sql语句也作为固定的写法,只传递where条件后面的参数就可以了,可以看到泛型方法很有用处
但是有的时候定义了泛型方法,却希望他只能用于某一种特定的类型,上述的例子可以用于所有泛型类型,但是如果我要封装的不是底层,只是某一个高级方法,只允许某一种类型使用这个方法,那么该如何做呢?可以使用泛型约束
泛型约束
1 public static T WhereGet<T>(string sql, IList<SqlParameter> sqlParameters = null, IDbTransaction transaction = null) 2 where T:IList<T> 3 { 4 DataTable table = SQLServerHelper.Query(sql, sqlParameters, transaction); 5 if (table != null && table.Rows != null && table.Rows.Count > 0) 6 { 7 DataRow row = table.Rows[0]; 8 return ConvertRowToModel<T>(row, table.Columns); 9 } 10 else 11 { 12 Type modeType = typeof(T); 13 return default(T); 14 } 15 16 }
2016710101282017-10-30《java程序设计》之泛型程序设计
一、基本概念泛型也被称为参数化类型(parameterizedtype),就是在定义类、接口和方法时,通过类型参数指示将要处理的对象类型。(如ArrayList类)泛型程序设计(Genericprogramming)意味着编写代码可以被很多不同类型的对象所重用... 查看详情
typescript专题之泛型
...范式。允许我们在编写代码的时候使用一些以后才指定的类型,在实例化时作为参数指明这些类型。而不同语言对于泛型的实现是不同的。泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方... 查看详情
java之泛型解说
1、集合中只能装入引用数据类型,不能装入基本数据类型。如,装入int类型的数值123会自动装箱。2、开发人员装入集合的数据类型不确定,所以它被设计成可以装入所有的Object。3、新的问题产生,装入集合的数据丢失原来的数... 查看详情
泛型之泛型方法
...型方法*当使用泛型类时,必须在创建对象实例的时候指定类型参数的值*而使用泛型方法的时候,通常不必指明参数类型,编译器会为我们找出具体的类型--->类型参数推断typeargumentinference*@authorYoujie**/publicclassFoo{/***方法push像被... 查看详情
java之泛型擦除
...除 在严格的泛型代码里,带泛型声明的类总应该带着类型参数。但为了与老的Java代码保持一致,也允许在使用带泛型声明的类时不指定类型参数。如果没有为这个泛型类指定泛型参数,则该类型参数被称作一个原始类型(ra... 查看详情
java基础--泛型之泛型参数
泛型机制常用的参数有3个:“?”代表任意类型。如果只指定了<?>,而没有extends,则默认是允许任意类。extends关键字声明了类型的上界,表示参数化的类型可能是所指定的类型,或者是此类型的子类。super关键字声明了... 查看详情
java之泛型
...原因:集合中可以存储各种对象,会被自动被提升为Object类型,当我们在取出每一个对象时,需要进行相应的操作。但如果集合中存储着不同类型的元素,则会出现类型转换异常ClassCastException,所以在存储时,必须明确集合元素... 查看详情
java进阶之泛型
泛型的优点泛型类型可以提高可靠性和可读性泛型可以参数化类型泛型可以让我们在编译时而不是在运行时检测出错误泛型类或方法允许用户指定可以和这些类或方法一起工作的对象类型(相容的对象)泛型类ArrayList、泛型接口... 查看详情
c#之泛型
...int数据,另一个是处理string数据,或者其他自定义的数据类型,但我们没有办法,只能分别写多个方法处理每个数据类型,因为方法的参数类型不同。有没有一种办法,在方法中传入通用的数据类型,这样不就可以合并代码了吗... 查看详情
学习系列之泛型
...更多的解释是通用的意思,然而有些人会认为明明是通用类型,怎么成泛型了的,其实这两者并不冲突的,泛型本来代表的就是通用类型,只是微软可能有一个比较官方的此来形容自己引入的特性而已,既然泛型是通用的,那么... 查看详情
java基础知识(java之泛型)
...p;什么是泛型?为什么要使用泛型?泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化... 查看详情
java之泛型
...相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。3、泛型的使用1..泛型类2.泛型方法3.泛型接口3.1泛型类publicclassPerson{publicstaticvoidmain(String[]arg 查看详情
2022-08-01java之泛型枚举多线程
...型1.什么是泛型泛型是JavaSE5出现的新特性,泛型的本质是类型参数化或参数化类型,在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型。2.泛型的意义一般的类和方法,只能使用具体的类型:要么... 查看详情
java重要技术(19)泛型之泛型的使用方法
1.1. 泛型的用法参数化类型比如Sample<T>中的类型参数T可以用于构造函数的参数的类型,方法的返回值的类型,画着方法的参数的类型,属性字段的类型等。publicclassGenericParameterTest{//参数化类型T,可以为String,Integer等引用... 查看详情
c#之泛型详解
...int数据,另一个是处理string数据,或者其他自定义的数据类型,但我们没有办法,只能分别写多个方法处理每个数据类型,因为方法的参数类型不同。有没有一种办法,在方法中传入通用的数据类型,这样不就可以合并代码了吗... 查看详情
java语法糖之泛型与类型擦除(代码片段)
1泛型与类型擦除泛型,JDK1.5新特性,本质是参数化类型(ParametersizedType)的应用,即所操作的数据类型被指定为一个参数。这种参数类型可用在:类接口方法的创建中,分别称为:泛型类泛型接口泛型方法在Java还... 查看详情
java遗珠之泛型类型擦除(代码片段)
...型的作用之前已经介绍过了只是用于编译之前更为严格的类型检查,其他的一些特性也都是编译之前的,在编译之后泛型是会被擦除掉的。类型擦除所做的事情如下:如果是无界限的则会把类型参数替换成Object,... 查看详情
十:java之泛型
...要有下面两种: 在程序编码中一些包括类型參数的类型,也就是说泛型的參数仅仅能够代表类。不能代表个别对象。(这是当今较常见的定义)在程序编码中一些包括參数的类。其參数能够代表类或对象等等。... 查看详情