Heim  >  Artikel  >  Backend-Entwicklung  >  Generisches .NET-Parsing

Generisches .NET-Parsing

高洛峰
高洛峰Original
2016-12-19 15:55:131413Durchsuche

【1】: Einführung in Generika

Generika sind eine wichtige neue Funktion in C# 2.0. Generika sind ein spezieller Mechanismus, der von CLR und Programmiersprachen bereitgestellt wird. Er unterstützt eine andere Form der Code-Wiederverwendung. Generika werden normalerweise zusammen mit Sammlungen und Methoden verwendet, die auf Sammlungen angewendet werden. Sie können natürlich auch einzeln verwendet werden.

C# ist eine stark typisierte Sprache. Bevor Generika vorgeschlagen wurden, haben wir bei der Konvertierung Sammlungscode verwendet , ist jedes Mal eine implizite Umwandlung erforderlich. Wir alle wissen, dass die endgültige Basisklasse aller Objekte jedes Mal, wenn wir ein Objekt verwenden, umgewandelt werden muss.
Bei Generika müssen wir bei der Verwendung von Generika keine Konvertierung durchführen, da Generika jeden Typ basierend auf dem vom Empfänger übergebenen Parametertyp direkt in den übergebenen Parametertyp ändern.
Allgemeine Situation Nachfolgend der Erstellungsprozess Eine generische Klasse besteht darin, mit einer vorhandenen konkreten Klasse zu beginnen und jeden Typ nacheinander in einen Typparameter zu ändern, bis das beste Gleichgewicht zwischen Generalisierung und Benutzerfreundlichkeit erreicht ist. Achten Sie beim Erstellen Ihrer eigenen generischen Klassen besonders auf Folgendes:

Welche Typen werden in Typparameter verallgemeinert?

Im Allgemeinen gilt: Je mehr Typen parametrisiert werden können, desto flexibler und wiederverwendbarer wird der Code. Eine zu starke Verallgemeinerung kann es jedoch anderen Entwicklern erschweren, den Code zu lesen oder zu verstehen.

Wenn Einschränkungen vorhanden sind, welche Einschränkungen sollten auf die Typparameter angewendet werden

Eine nützliche Regel besteht darin, so viele Einschränkungen wie möglich anzuwenden, die es Ihnen dennoch ermöglichen, mit den Typen umzugehen, mit denen Sie arbeiten müssen . Wenn Sie beispielsweise wissen, dass Ihre generische Klasse nur für Referenztypen verwendet wird, wenden Sie Klasseneinschränkungen an. Dies verhindert, dass Ihre Klasse versehentlich als Werttyp verwendet wird, und ermöglicht Ihnen die Verwendung des as-Operators für T sowie die Prüfung auf Nullwerte.

Ob generisches Verhalten in Basisklassen und Unterklassen zerlegt werden soll.

Da generische Klassen als Basisklassen verwendet werden können, gelten hier die gleichen Designüberlegungen wie für nicht generische Klassen. Weitere Informationen zu den Regeln zum Erben von generischen Basisklassen finden Sie weiter unten in diesem Thema.

Gibt an, ob eine oder mehrere generische Schnittstellen implementiert werden sollen.

Wenn Sie beispielsweise eine Klasse entwerfen, die zum Erstellen von Elementen in einer generischen Sammlung verwendet wird, müssen Sie möglicherweise eine Schnittstelle wie IComparable implementieren, wobei T der Typ Ihrer Klasse ist.

[2]: Darstellung von Generika

Generika können Referenztypen und Werttypen sowie Schnittstellen und Delegaten, FCL (DLL-Assembly, einschließlich verschiedener DLL-Klassen unter) sein Das .NET-Framework definiert eine generische Liste zum Verwalten einer Sammlung von Objekten. Wenn wir diese generische Liste verwenden möchten, können wir bei der Verwendung den spezifischen Datentyp angeben.
Generika werden durch „T“ dargestellt, wie zum Beispiel: List, T stellt einen nicht spezifizierten Datentyp dar. Wir können uns die Referenzdefinition von Generika in der FCL-Klasse ansehen:
System.Collections.Generic-Namespace Enthält Schnittstellen und Klassen, die generische Sammlungen definieren, die es Benutzern ermöglichen, stark typisierte Sammlungen zu erstellen, die eine bessere Typsicherheit und Leistung bieten als nicht generische stark typisierte Sammlungen.
Der Prozess der Erstellung einer generischen Klasse besteht darin, mit einer vorhandenen konkreten Klasse zu beginnen und jeden Typ nacheinander in einen Typparameter zu ändern, bis das beste Gleichgewicht zwischen Generalisierung und Benutzerfreundlichkeit erreicht ist

【3】: Vorteile von Generika

1: Machen Sie den Code prägnanter und klarer

Wie bereits erwähnt, sind Generika wiederverwendbar und reduzieren die Menge unseres Codes Entwickeln und pflegen Sie zum Beispiel:
Beispiel 1: Wenn wir die Datenbank aus der Datenbank abrufen, geben wir häufig einen DataTable-Typ zurück und konvertieren ihn dann in eine List-Sammlung. Wenn es also keine Generika gibt, mache ich das normalerweise so.

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 = &#39;"+userId+"&#39; AND T.ENDTIME > DATETIME(&#39;now&#39;, &#39;localtime&#39;) AND T.StartTime <= DATETIME(&#39;now&#39;, &#39;localtime&#39;) ;").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 der Methode DataTableToList übergeben wir ein DataTable-Objekt und durchlaufen dann den Objektwert jeder Zeile, um ihn dem Objekt vom Typ TrainingUser zuzuweisen. Dies ist nur eine der Methoden If Zu unseren Typen gehören auch Training/Benutzer/Projekt und andere Typen. Müssen wir viele Methoden wie DataTableToList schreiben, was zu Coderedundanz führt und das Problem der umständlichen Wartung löst?

Beispiel 2: Durch die Verwendung von Generika wird der obige Code klarer und prägnanter.

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 der obigen Methode haben wir eine generische Methode definiert, die in der internen Implementierung zum Konvertieren der Datentabelle verwendet wurde in eine Liste (zusammengefasst im Folgeaufsatz zur Reflexion, hier konzentrieren wir uns nur auf den generischen Teil). Wir haben einen statischen Rückgabewert als List definiert, wir sagten zuvor, dass T: einen beliebigen Typ darstellt (außer Aufzählung). ToList1, was bedeutet, dass wir beim Aufrufen dieser Methode dem Methodennamen auch einen Typwert zuweisen müssen, und dieser Typ muss mit seinem Rückgabewert identisch sein. Die Werttypen sind konsistent (Generika sind typsicher). Wo: verwendet Um die Bedingungen von T einzuschränken, z. B. wo T: Klasse, bedeutet new(), dass T nur eine Klasse oder ein Typobjekt sein kann, dann werden wir das tun Sie können es auf diese Weise tun

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 = &#39;"+ userId +"&#39; AND T.ENDTIME > DATETIME(&#39;now&#39;, &#39;localtime&#39;) AND T.StartTime <= DATETIME(&#39;now&#39;, &#39;localtime&#39;) ;").Tables[0]);
              return trainingUserList ;
        }

代码中的DataTableHelper.ToList1 即为我们刚才所写的一个泛型方法,这样我们可以看到,ToList 传入的类型为TrainingUser,同时接收者为:List = List ,
这样即便我们后续还有Training/User/Project等其他的类型,我们都可以直接使用DataTableHelper.ToList1(DataTable dt) 来进行类型转换.

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 来比较,结果为 :

Generisches .NET-Parsing

我们可以看到非泛型的时间要比泛型的时间多出0.0000523秒,泛型比非泛型的时间要多出一些, 那么我们将数值改动一下改为循环 1000次.得出结果为 :

Generisches .NET-Parsing

泛型比非泛型执行的时间要短0.0000405秒
我们将时间在改动一下,改为 100000呢?结果为 :

Generisches .NET-Parsing

这次差距为 0.0054621 并且随着执行次数的增长,非泛型相比泛型的时间会逐步的增加,
通过反编译我们也能看出 :
泛型:

Generisches .NET-Parsing

非泛型

Generisches .NET-Parsing

从编译中我们也能看出泛型方法中,接收的为Int32,非泛型为Object,其次泛型不会进行装箱和拆箱操作,非泛型每次执行都要进行装箱和拆箱操作.

3 : 类型安全

在实例1 , 2 ,3 中我们都有备注说明,泛型的发送着必须要和接收者进行一致,否则会报异常 ,例如 :

实例 4 :

Generisches .NET-Parsing

首页

最新文章

IT 职场

前端

后端

移动端

数据库

运维

其他技术

- 导航条 -首页最新文章IT 职场前端- JavaScript- HTML5- CSS后端- Python- Java- C/C++- PHP- .NET- Ruby- Go移动端- Android- iOS数据库运维- Linux- UNIX其他技术- Git- 机器学习- 算法- 测试- 信息安全- Vim

伯乐在线 > 首页 > 所有文章 > 开发 > Generisches .NET-Parsing(上)

Generisches .NET-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, T 表示一个未指定的数据类型,我们可以看一下FCL类中泛型的引用定义:
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 = &#39;"+userId+"&#39; AND T.ENDTIME > DATETIME(&#39;now&#39;, &#39;localtime&#39;) AND T.StartTime <= DATETIME(&#39;now&#39;, &#39;localtime&#39;) ;").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 ,前面我们说过 T : 代表任意类型(枚举除外),ToList1,说明我们在调用这个方法的时候,同时要赋予方法名一个类型值,这个类型要和它的返回值类型一致(泛型是类型安全的),Where : 用于限制T的条件 ,例如 where T : class,new() 表示 T 只能是一个类,或者一个类型对象,那么我们在调用的时候就可以这样来

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 = &#39;"+ userId +"&#39; AND T.ENDTIME > DATETIME(&#39;now&#39;, &#39;localtime&#39;) AND T.StartTime <= DATETIME(&#39;now&#39;, &#39;localtime&#39;) ;").Tables[0]);
              return trainingUserList ;
        }

   

代码中的DataTableHelper.ToList1 即为我们刚才所写的一个泛型方法,这样我们可以看到,ToList 传入的类型为TrainingUser,同时接收者为:List = List ,
这样即便我们后续还有Training/User/Project等其他的类型,我们都可以直接使用DataTableHelper.ToList1(DataTable dt) 来进行类型转换.

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 并且随着执行次数的增长,非泛型相比泛型的时间会逐步的增加,
通过反编译我们也能看出 :
泛型:

非泛型

 

Aus der Kompilierung können wir auch ersehen, dass die generische Methode Int32 empfängt und der nicht generische Typ Object ist. Zweitens führt der generische Typ keine Boxing- und Unboxing-Vorgänge aus und der nicht generische Typ muss jedes Mal geladen werden Es wird ausgeführt. Boxing- und Unboxing-Vorgänge.

3: Typsicherheit

In den Beispielen 1, 2 und 3 haben wir alle Bemerkungen, dass der Absender des Generikums mit dem Empfänger übereinstimmen muss , andernfalls werden Ausnahmen gemeldet, zum Beispiel:

Beispiel 4:

Beim Anwenden eines generischen Algorithmus auf einen bestimmten Typ können der Compiler und die CLR die Absicht des Entwicklers verstehen und stellen Sie sicher, dass nur Objekte, die mit dem angegebenen Datentyp kompatibel sind, mit dem Algorithmus verwendet werden können. Wenn Sie versuchen, ein Objekt eines inkompatiblen Typs zu verwenden, führt dies zu einem Fehler bei der Kompilierung oder es wird zur Laufzeit eine Ausnahme ausgelöst



Weitere Artikel zur generischen .NET-Analyse finden Sie auf der chinesischen PHP-Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn