>  기사  >  백엔드 개발  >  .NET 일반 구문 분석

.NET 일반 구문 분석

高洛峰
高洛峰원래의
2016-12-19 15:55:131411검색

【1】: 제네릭 소개

제네릭은 C# 2.0의 중요한 새로운 기능입니다. 제네릭은 CLR 및 프로그래밍 언어에서 제공하는 특수 메커니즘입니다. 제네릭은 일반적으로 컬렉션 및 컬렉션에서 작동하는 메서드와 함께 사용됩니다. 물론 단독으로 사용할 수도 있습니다.

C#은 제네릭이 제안되기 전에는 컬렉션 코드를 사용하여 변환했습니다. , 모든 객체의 최종 기본 클래스는 object라는 것을 모두 알고 있습니다. 어떤 유형으로 변환되든 상관없이 캐스트되어야 합니다.
제네릭을 사용하면 제네릭을 사용할 때 변환할 필요가 없습니다. 제네릭은 수신자가 전달한 매개변수 유형을 기반으로 각 유형을 전달된 매개변수 유형으로 직접 변경하기 때문입니다.
일반적인 상황 아래에서는 생성 과정을 설명합니다. 일반 클래스는 기존의 구체적인 클래스로 시작하여 일반화와 유용성의 최상의 균형이 달성될 때까지 각 유형을 유형 매개변수로 하나씩 변경하는 것입니다. 자신만의 제네릭 클래스를 만들 때 다음 사항에 특히 주의하세요.

유형 매개변수로 일반화되는 유형은 무엇인가요?

일반적으로 매개변수화할 수 있는 유형이 많을수록 코드의 유연성과 재사용성이 높아집니다. 그러나 지나치게 일반화하면 다른 개발자가 코드를 읽거나 이해하기 어려울 수 있습니다.

제약조건이 있는 경우 유형 매개변수에 어떤 제약조건을 적용해야 하는지

유용한 규칙은 처리해야 하는 유형을 처리할 수 있도록 가능한 한 많은 제약조건을 적용하는 것입니다. . 예를 들어, 제네릭 클래스가 참조 유형에만 사용된다는 것을 알고 있다면 클래스 제약 조건을 적용하세요. 이렇게 하면 클래스가 실수로 값 유형으로 사용되는 것을 방지하고 T에서 as 연산자를 사용하고 null 값을 확인할 수 있습니다.

일반 동작을 기본 클래스와 하위 클래스로 분해할지 여부입니다.

제네릭 클래스를 기본 클래스로 사용할 수 있으므로 여기에는 제네릭이 아닌 클래스와 동일한 디자인 고려 사항이 적용됩니다. 이 항목의 뒷부분에 나오는 일반 기본 클래스에서 상속하는 규칙을 참조하세요.

하나 이상의 일반 인터페이스를 구현할지 여부입니다.

예를 들어 제네릭 기반 컬렉션에서 항목을 생성하는 데 사용할 클래스를 디자인하는 경우 IComparable과 같은 인터페이스를 구현해야 할 수 있습니다. 여기서 T는 클래스 유형입니다.

[2]: 제네릭의 표현

제네릭은 참조 유형 및 값 유형은 물론 인터페이스 및 대리자, FCL(DLL 어셈블리, 아래의 다양한 DLL 클래스 포함)일 수 있습니다. .NET 프레임워크는 개체 컬렉션을 관리하기 위해 일반 목록을 정의합니다. 이 일반 목록을 사용하려는 경우 이를 사용할 때 특정 데이터 형식을 지정할 수 있습니다.
제네릭은 List와 같이 "T"로 표시됩니다. T는 지정되지 않은 데이터 유형을 나타냅니다. FCL 클래스에서 제네릭의 참조 정의를 볼 수 있습니다.
System.Collections.Generic 네임스페이스 인터페이스를 포함합니다. 사용자가 제네릭이 아닌 강력한 형식의 컬렉션보다 더 나은 형식 안전성과 성능을 제공하는 강력한 형식의 컬렉션을 만들 수 있도록 하는 일반 컬렉션을 정의하는 클래스가 있습니다.
제네릭 클래스를 만드는 과정은 기존의 구체적인 클래스에서 시작하여 일반화와 유용성의 최상의 균형이 달성될 때까지 각 유형을 하나씩 유형 매개변수로 변경하는 것입니다.

【3】: 제네릭의 장점

1: 코드를 더욱 간결하고 명확하게 만듭니다.

앞서 언급했듯이 제네릭은 재사용이 가능하고 프로그램의 코드 양을 줄여줍니다. 예를 들어 개발 및 유지 관리:
예제 1: 데이터베이스에서 데이터베이스를 가져올 때 DataTable 유형을 반환한 다음 이를 List 컬렉션으로 변환하는 경우가 많습니다. 그래서 제네릭이 없으면 보통 이렇게 합니다.

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 유형 객체에 할당합니다. 이는 If 메소드 중 하나일 뿐입니다. 우리 유형에는 Training/User/Project 및 기타 유형도 포함됩니다. DataTableToList와 같은 많은 메소드를 작성해야 합니까? 이는 이 메소드를 반영하며 이로 인해 코드 중복이 발생하고 불편한 유지 관리 문제가 발생하므로 제네릭을 사용하여 해결합니다.

예시 2: 제네릭을 사용하면 위 코드가 더욱 명확해지고 간결해집니다.

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로 정의했습니다. 앞서 T:는 모든 유형을 나타냅니다(열거형 제외). ToList1, 이는 이 메소드를 호출할 때 메소드 이름에 유형 값도 제공해야 하며 이 유형은 반환 값과 동일해야 함을 의미합니다. 값 유형은 일관됩니다(제네릭은 유형 안전함). 여기서: 사용됨 T: class와 같이 T의 조건을 제한하려면 new()는 T가 클래스 또는 유형 객체만 될 수 있다는 것을 의미하므로 다음과 같이 할 수 있습니다.

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

.NET 일반 구문 분석

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

.NET 일반 구문 분석

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

.NET 일반 구문 분석

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

.NET 일반 구문 분석

非泛型

.NET 일반 구문 분석

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

3 : 类型安全

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

实例 4 :

.NET 일반 구문 분석

首页

最新文章

IT 职场

前端

后端

移动端

数据库

运维

其他技术

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

伯乐在线 > 首页 > 所有文章 > 开发 > .NET 일반 구문 분석(上)

.NET 일반 구문 분석(上)

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

非泛型

 

컴파일을 보면 제네릭 메서드가 Int32를 수신하고 제네릭이 아닌 유형이 Object라는 것을 알 수 있습니다. 둘째, 제네릭 유형은 박싱 및 언박싱 작업을 수행하지 않으며 제네릭이 아닌 유형은 매번 로드해야 합니다. 박싱 및 언박싱 작업이 실행됩니다.

3: 유형 안전성

예제 1, 2, 3에는 제네릭의 발신자가 수신자와 일치해야 한다는 언급이 있습니다. , 그렇지 않으면 예외가 보고됩니다. 예:

예 4:

특정 유형에 일반 알고리즘을 적용할 때 컴파일러와 CLR은 개발자의 의도를 이해할 수 있습니다. , 지정된 데이터 유형과 호환되는 객체만 알고리즘과 함께 사용할 수 있는지 확인하세요. 호환되지 않는 유형의 객체를 사용하려고 하면 컴파일 시간 오류가 발생하거나 런타임 시 예외가 발생합니다.



.NET 일반 분석 관련 더 많은 글은 PHP 중국어 홈페이지를 주목해주세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.