Home > Article > Backend Development > .NET generic parsing
[1]: Introduction to generics
Generics are an important new feature in C# 2.0. Generics are a special mechanism provided by the CLR and programming languages, which support another form of code reuse. Generics are usually used together with collections and methods that operate on collections. Of course, they can also be used alone.
C# is a strongly typed language. Before generics were proposed, when we used collection code, we always Converting it every time requires implicit cast. We all know that the final base class of all objects is object. Every time we use object, we must cast it no matter what type it is converted to.
With generics, we don’t need to convert when using generics, because generics directly change each type to the passed parameter type based on the parameter type passed in by the receiver.
Generally, creating a generic The process of type typing is to start with an existing concrete class and change each type to type parameters one by one until the best balance of generalization and usability is achieved. When creating your own generic classes, you need to pay special attention to the following:
Which types are generalized into type parameters.
Generally, the more types that can be parameterized, the more flexible and reusable the code becomes. However, too much generalization can make it difficult for other developers to read or understand the code.
If constraints exist, what constraints should be applied to the type parameters?
A useful rule is to apply as many constraints as possible that still enable you to deal with the types you have to deal with. For example, if you know that your generic class will only be used for reference types, apply class constraints. This prevents your class from being accidentally used as a value type and allows you to use the as operator on T as well as check for null values.
Whether to decompose generic behavior into base classes and subclasses.
Since generic classes can be used as base classes, the same design considerations apply here as for non-generic classes. See the rules for inheriting from generic base classes later in this topic.
Whether to implement one or more generic interfaces.
For example, if you design a class that will be used to create items in a generic-based collection, you may have to implement an interface like IComparable, where T is the type of your class.
[2]: Expression method of generics
Generics can be reference types and value types as well as interfaces and delegates, and are defined in FCL (DLL assembly, including various DLLs under the .NET framework) class A generic list is created to manage a collection of objects. If we want to use this generic list, we can specify the specific data type when using it.
Generics are expressed as "T" such as: List
System.Collections.Generic namespace contains definitions of generics Collection interfaces and classes, generic collections allow users to create strongly typed collections, which provide better type safety and performance than non-generic strongly typed collections.
The process of creating a generic class is: start with an existing concrete class, and change each type to a type parameter one by one until the best balance of generalization and usability is achieved
[3]: Benefits of generics
1: Make the code more concise and clear
As we mentioned earlier, generics are reusable, reduce the amount of our code, and make our programs easier to develop and maintain. Example:
Example 1: From When fetching the database from the database, we often return a DataTable type and then convert it into a List collection. So if there are no generics, I will usually do it like this.
public List<TrainingUser>GetTrainingUser(string userId) { DataTable dt = SqliteHelper.ExecuteDataset(System.Data.CommandType.Text, @" SELECT DISTINCT UserId,TrainingId FROM TRAININGUSER AS TU INNER JOIN [USER] AS U ON U.ID = TU.USERID JOIN [TRAINING] AS T ON T.ID = TU.TRAININGID WHERE U.ID = '"+userId+"' AND T.ENDTIME > DATETIME('now', 'localtime') AND T.StartTime <= DATETIME('now', 'localtime') ;").Tables[0]; return DataTableToList(dt); } private List<TrainingUser> DataTableToList(DataTabledt) { List<TrainingUser> list = new List<TrainingUser>(); if(dt. Rows.Count > 0 ) { foreach (DataRow row in dt .Rows) { TrainingUser trainingUser = new TrainingUser(); if(row["UserId" ] != null) { trainingUser .UserId = row["UserId"].ToString(); } if(row["TrainingId" ] != null) { trainingUser.TrainingId = row["TrainingId"].ToString(); } list.Add(trainingUser); } } return list; }
In the method DataTableToList, we pass in A DataTable object, and then loop through the object value of each row to assign it to the TrainingUser type object. This is just one of the methods. If our type also has Training/User/Project and other types, do we have to What about writing many methods like DataTableToList? This reflects this method, which will cause code redundancy and inconvenient maintenance. Then we use generics to solve the problem
Example 2: Use generics to make the above code more visible Clear and concise
public static List<T> ToList1<T>(DataTable dt) whereT : class, new() { var prlist =new List<PropertyInfo>(); Type type = typeof(T); Array.ForEach( type.GetProperties(), p => { if(dt.Columns.IndexOf(p.Name) !=-1) { prlist.Add(p); } }); var oblist = new List<T>(); // System.Data.SqlTypes. foreach(DataRow row in dt.Rows) { var ob = new T(); prlist.ForEach( p => { if(row[p.Name] != DBNull.Value) { p.SetValue(ob, row[p.Name], null); } }); oblist.Add(ob); } return oblist; }
In the above method, we define a generic method. The internal implementation uses the principle of reflection to convert the DataTable into a List (summarized in the follow-up essay on reflection, here we only focus on generics (Part is enough), we defined a static return value as List
public List<TrainingUser>GetTrainingIdByUserId(string userId) { List<TrainingUser> trainingUserList = DataTableHelper.ToList1<TrainingUser>( SqliteHelper.ExecuteDataset(System.Data.CommandType.Text, @" SELECT DISTINCT UserId,TrainingId FROM TRAININGUSER AS TU INNER JOIN [USER] AS U ON U.ID = TU.USERID JOIN [TRAINING] AS T ON T.ID = TU.TRAININGID WHERE U.ID = '"+ userId +"' AND T.ENDTIME > DATETIME('now', 'localtime') AND T.StartTime <= DATETIME('now', 'localtime') ;").Tables[0]); return trainingUserList ; }
代码中的DataTableHelper.ToList1
这样即便我们后续还有Training/User/Project等其他的类型,我们都可以直接使用DataTableHelper.ToList1
2 : 提升程序的性能
泛型与非泛型相比较而言,性能要好一些,这是为什么? 首先,泛型的类型是由调用者(接收者),去直接赋值的(类型安全的), 那么就不会存在类型转换的问题,其次, 泛型减少了装箱和拆箱的过程.
实例 3 : 对于值类型泛型与非泛型的性能比较
private static void ListTest() { List<int>list = new List<int>(); for(inti = 0; i < 100; i++) { list.Add(i); int a = list[i]; } list =null; } private static void ArrListTest() { ArrayList arr = new ArrayList(); for(inti = 0; i <100; i++) { arr.Add(i); int s = (int)arr[i]; } arr = null; } Stopwatch sw = new Stopwatch(); sw.Start(); ListTest(); Console.WriteLine(" 使用泛型List执行值类型方法历时 : "+ sw.Elapsed.ToString()); sw.Stop(); Stopwatch sw1 = new Stopwatch(); sw1.Start(); ArrListTest(); Console.WriteLine(" 使用非泛型ArrayList执行值类型方法历时 : "+ sw1.Elapsed.ToString()); sw1.Stop(); Console.ReadLine();
通过循环 100 来比较,结果为 :
我们可以看到非泛型的时间要比泛型的时间多出0.0000523秒,泛型比非泛型的时间要多出一些, 那么我们将数值改动一下改为循环 1000次.得出结果为 :
泛型比非泛型执行的时间要短0.0000405秒
我们将时间在改动一下,改为 100000呢?结果为 :
这次差距为 0.0054621 并且随着执行次数的增长,非泛型相比泛型的时间会逐步的增加,
通过反编译我们也能看出 :
泛型:
非泛型
从编译中我们也能看出泛型方法中,接收的为Int32,非泛型为Object,其次泛型不会进行装箱和拆箱操作,非泛型每次执行都要进行装箱和拆箱操作.
3 : 类型安全
在实例1 , 2 ,3 中我们都有备注说明,泛型的发送着必须要和接收者进行一致,否则会报异常 ,例如 :
实例 4 :
首页
最新文章
IT 职场
前端
后端
移动端
数据库
运维
其他技术
- 导航条 -首页最新文章IT 职场前端- JavaScript- HTML5- CSS后端- Python- Java- C/C++- PHP- .NET- Ruby- Go移动端- Android- iOS数据库运维- Linux- UNIX其他技术- Git- 机器学习- 算法- 测试- 信息安全- Vim
伯乐在线 > 首页 > 所有文章 > 开发 > .NET generic parsing(上)
.NET generic parsing(上)
2015/07/03 · 开发 · .Net
分享到:6
拍摄与剪辑“怀孕日记”
PS大神通关教程
PS入门基础-魔幻调色
慕课网技术沙龙之前端专场
原文出处: 刘彬的博客
【1】:泛型介绍
泛型是C#2.0中一个重要的新特性,泛型是CLR和编程语言提供的一种特殊机制,它支持另一种形式的代码重用。泛型通常用与集合以及作用于集合的方法一起使用,当然也可以单独使用.
C#是一种强类型的语言,在泛型没有被提出之前,我们在使用集合的代码的时候,每次对其进行转换都需要隐式的强制转换,我们都知道所有对象的最终基类是object,我们在每次使用object的时候,无论是变换什么类型都要对其进行强制转换。
那么有了泛型之后,使用泛型我们就无需进行转换,因为泛型根据接收者传入的参数类型,直接将每个类型更改为所传入的参数类型.
一般情况下,创建泛型类的过程为:从一个现有的具体类开始,逐一将每个类型更改为类型参数,直至达到通用化和可用性的最佳平衡。 创建您自己的泛型类时,需要特别注意以下事项:
将哪些类型通用化为类型参数。
通常,能够参数化的类型越多,代码就会变得越灵活,重用性就越好。 但是,太多的通用化会使其他开发人员难以阅读或理解代码。
如果存在约束,应对类型参数应用什么约束
一条有用的规则是,应用尽可能最多的约束,但仍使您能够处理必须处理的类型。 例如,如果您知道您的泛型类仅用于引用类型,则应用类约束。 这可以防止您的类被意外地用于值类型,并允许您对 T 使用 as 运算符以及检查空值。
是否将泛型行为分解为基类和子类。
由于泛型类可以作为基类使用,此处适用的设计注意事项与非泛型类相同。 请参见本主题后面有关从泛型基类继承的规则。
是否实现一个或多个泛型接口。
例如,如果您设计一个类,该类将用于创建基于泛型的集合中的项,则可能必须实现一个接口,如 IComparable,其中 T 是您的类的类型。
【2】:泛型的表示方式
泛型可以为引用类型和值类型还有接口和委托,FCL( DLL程序集,包含了.NET框架下的各种DLL )类中定义了一个泛型列表,用来管理一个对象集合,如果我们想要使用这个泛型列表,可以在使用时指定具体数据类型。
泛型的表示为 “T” 如:List
System.Collections.Generic 命名空间包含定义泛型集合的接口和类,泛型集合允许用户创建强类型集合,它能提供比非泛型强类型集合更好的类型安全性和性能。
创建泛型类的过程为:从一个现有的具体类开始,逐一将每个类型更改为类型参数,直至达到通用化和可用性的最佳平衡
【3】:泛型的好处
1 : 使代码更加的简洁,清晰
从前面我们也提到了,泛型具备可重用性 , 减少我们代码量, 使我们的程序更容易开发和维护,举例 :
实例 1 : 在从数据库中获取数据库的时候,我们经常会返回一个DataTable类型,然后将其转换为List集合. 那么如果没有泛型的话,我会一般会这样来做.
C#
public List<TrainingUser>GetTrainingUser(string userId) { DataTable dt = SqliteHelper.ExecuteDataset(System.Data.CommandType.Text, @" SELECT DISTINCT UserId,TrainingId FROM TRAININGUSER AS TU INNER JOIN [USER] AS U ON U.ID = TU.USERID JOIN [TRAINING] AS T ON T.ID = TU.TRAININGID WHERE U.ID = '"+userId+"' AND T.ENDTIME > DATETIME('now', 'localtime') AND T.StartTime <= DATETIME('now', 'localtime') ;").Tables[0]; return DataTableToList(dt); } private List<TrainingUser> DataTableToList(DataTabledt) { List<TrainingUser> list = new List<TrainingUser>(); if(dt. Rows.Count > 0 ) { foreach (DataRow row in dt .Rows) { TrainingUser trainingUser = new TrainingUser(); if(row["UserId" ] != null) { trainingUser .UserId = row["UserId"].ToString(); } if(row["TrainingId" ] != null) { trainingUser.TrainingId = row["TrainingId"].ToString(); } list.Add(trainingUser); } } return list; }
在方法DataTableToList中,我们传入了一个DataTable的对象,然后在去循环遍历每一行的对象值从而去赋值给TrainingUser类型对象,这只是其中的一个方法,如果我们的类型还有 Training/User/Project等类型的话,我们是不是就要写很多如同DataTableToList这样的方法呢? 这就体现出了这样的方式,会造成代码的冗余以及维护不便问题,那么我们使用泛型来解决
实例 2 : 使用泛型使上面的代码更见的清晰,简洁
C#
public static List<T> ToList1<T>(DataTable dt) whereT : class, new() { var prlist =new List<PropertyInfo>(); Type type = typeof(T); Array.ForEach( type.GetProperties(), p => { if(dt.Columns.IndexOf(p.Name) !=-1) { prlist.Add(p); } }); var oblist = new List<T>(); // System.Data.SqlTypes. foreach(DataRow row in dt.Rows) { var ob = new T(); prlist.ForEach( p => { if(row[p.Name] != DBNull.Value) { p.SetValue(ob, row[p.Name], null); } }); oblist.Add(ob); } return oblist; }
在上面的这个方法中,我们定义了一个泛型方法,内部实现中是使用了反射的原理,将DataTable转换为了List(反射后续随笔中总结,此处只关注泛型部分即可),我们定义了一个静态的返回值为List
C#
public List<TrainingUser>GetTrainingIdByUserId(string userId) { List<TrainingUser> trainingUserList = DataTableHelper.ToList1<TrainingUser>( SqliteHelper.ExecuteDataset(System.Data.CommandType.Text, @" SELECT DISTINCT UserId,TrainingId FROM TRAININGUSER AS TU INNER JOIN [USER] AS U ON U.ID = TU.USERID JOIN [TRAINING] AS T ON T.ID = TU.TRAININGID WHERE U.ID = '"+ userId +"' AND T.ENDTIME > DATETIME('now', 'localtime') AND T.StartTime <= DATETIME('now', 'localtime') ;").Tables[0]); return trainingUserList ; }
代码中的DataTableHelper.ToList1
这样即便我们后续还有Training/User/Project等其他的类型,我们都可以直接使用DataTableHelper.ToList1
2 : 提升程序的性能
泛型与非泛型相比较而言,性能要好一些,这是为什么? 首先,泛型的类型是由调用者(接收者),去直接赋值的(类型安全的), 那么就不会存在类型转换的问题,其次, 泛型减少了装箱和拆箱的过程.
实例 3 : 对于值类型泛型与非泛型的性能比较
C#
private static void ListTest() { List<int>list = new List<int>(); for(inti = 0; i < 100; i++) { list.Add(i); int a = list[i]; } list =null; } private static void ArrListTest() { ArrayList arr = new ArrayList(); for(inti = 0; i <100; i++) { arr.Add(i); int s = (int)arr[i]; } arr = null; } Stopwatch sw = new Stopwatch(); sw.Start(); ListTest(); Console.WriteLine(" 使用泛型List执行值类型方法历时 : "+ sw.Elapsed.ToString()); sw.Stop(); Stopwatch sw1 = new Stopwatch(); sw1.Start(); ArrListTest(); Console.WriteLine(" 使用非泛型ArrayList执行值类型方法历时 : "+ sw1.Elapsed.ToString()); sw1.Stop(); Console.ReadLine();
通过循环 100 来比较,结果为 :
我们可以看到非泛型的时间要比泛型的时间多出0.0000523秒,泛型比非泛型的时间要多出一些, 那么我们将数值改动一下改为循环 1000次.得出结果为 :
泛型比非泛型执行的时间要短0.0000405秒
我们将时间在改动一下,改为 100000呢?结果为 :
这次差距为 0.0054621 并且随着执行次数的增长,非泛型相比泛型的时间会逐步的增加,
通过反编译我们也能看出 :
泛型:
非泛型
From the compilation, we can also see that in the generic method, what is received is Int32, and the non-generic type is Object. Secondly, the generic type does not perform boxing and unboxing operations, and the non-generic type must be boxed and unboxed every time it is executed. Unboxing operation.
3: Type safety
In examples 1, 2, and 3, we have notes that the sender of generics must be consistent with the receiver, otherwise an exception will be reported, for example:
Example 4 :
When applying a generic algorithm to a specific type, the compiler and CLR can understand the developer's intentions and ensure that only objects compatible with the specified data type can be used with the algorithm. If you try to use an incompatible type An object will cause a compile-time error, or throw an exception at runtime
For more articles related to .NET generic analysis, please pay attention to the PHP Chinese website!