C#泛型---理解与分析

在这篇文章中,我们将会讨论如何在C#中用示例实现泛型。在这篇文章 中,我们将会讨论以下几个观点:

1、为什么需要泛型?

2、泛型是什么?

3、泛型的优势或者说优点;

4、如何实现泛型?

5、如何使用泛型?

一、为什么需要泛型?

我们用示例来说明一下为什么需要泛型。我们创建一个简单的项目来检查一下两个整数是否相等。下面这段代码很简单易懂。在这里,我们创建了两个类:ClsCalculator 和ClsMain,在ClsCxlculator类内,AreEqual()方法有两个整型值做为输入参数,并检查输入的两个参数是否相等,如果两者相等就返回true,否则就返回false。ClsMain类中,调用了静态方法AreEqual(),基于比较后的返回值进行输出。

 1 namespace GenericsDemo
 2 {
 3     public class ClsMain
 4     {
 5         private static void Main()
 6         {
 7             bool IsEqual = ClsCalculator.AreEqual(10, 20);
 8             if (IsEqual)
 9             {
10                 Console.WriteLine("Both are Equal");
11             }
12             else
13             {
14                 Console.WriteLine("Both are Not Equal");
15             }
16 
17             Console.ReadKey();
18         }
19     }
20 
21     public class ClsCalculator
22     {
23         public static bool AreEqual(int value1, int value2)
24         {
25             return value1 == value2;
26         }
27     }
28 }

上面的AreEqual()函数如期望一样正常运行,更重要地,做为最初的需求,它只能使用两个整型数值运行。假设我们的需求变化 了,现在我们需要比较两个string元素是否相等。

上述示例中,假如我们不是用整型数值进行比较,我们会得到一个编译错误。这是因为AreEqual()函数是绑定了整型类型的,因此,除了整型数据类型是不可能调用AreEqual函数的。所以,当我们试图通过用下面所示的string数据类型调用AreEqual()函数时,我们会收到一个编译错误。

bool Equal = ClsCalculator.AreEqual(“ABC”, “XYZ”);

为了让AreEqual()函数像接收整型那样接收string类型,我们需要使用object数据类型做为参数。如果用object数据类型做为AreEqual函数的参数,那么AreEqual函数就可以运行任何数据类型。

注:应该记住的最重要一点就是,所有.NET数据类型,无论是基本类型(primitive type)还是引用类型,他们都直接或间接地继承自System.Object数据类型。

用Object数据类型修改AreEqual()函数,如下所示:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 
 6 namespace Genrics
 7 {
 8     class Program
 9     {
10         static void Main(string[] args)
11         {
12             bool IsEqual = ClsCalculator.AreEqual("ABC","abc");
13             if (IsEqual)
14             {
15                 Console.WriteLine("Both is Equal.");
16             }
17             else
18              {
19                 Console.WriteLine("Both are Not Equal.");
20              }
21             Console.ReadKey();
22         }
23 
24     }
25     
26 
27     public class ClsCalculator
28     { 
29         //Now this method can accept any data type
30         public static bool AreEqual(object value1, object value2)
31         {
32             return value1 == value2;
33         }
34     }
35 }

 运行程序,会看到如上所期望的显示结果。我们再来看一下上述代码所呈现出来的问题:

1、由于拆箱和装箱,导致性能下降。而且,object类型需要转换成所需要的数据类型。

2、现在,AreEqual()函数不是类型安全的,可能第一个参数设置的是string类型,第二个参数是整数类型。

重载方法

另一个选择是重载AreEqual函数以接收不同类型的参数,如下所示。正如下面代码所示,现在我们已经创建了3种名称相同、带着不类型参数的函数,除了函数重载没有什么不同之处 。

 1 namespace GenericsDemo
 2 {
 3     public class ClsMain
 4     {
 5         private static void Main()
 6         {
 7            // bool IsEqual = ClsCalculator.AreEqual(10, 20);
 8            // bool IsEqual = ClsCalculator.AreEqual("ABC", "ABC");
 9             bool IsEqual = ClsCalculator.AreEqual(10.5, 20.5);
10 
11             if (IsEqual)
12             {
13                 Console.WriteLine("Both are Equal");
14             }
15             else
16             {
17                 Console.WriteLine("Both are Not Equal");
18             }
19 
20             Console.ReadKey();
21         }
22     }
23 
24     public class ClsCalculator
25     {
26         public static bool AreEqual(int value1, int value2)
27         {
28             return value1 == value2;
29         }
30 
31         public static bool AreEqual(string value1, string value2)
32         {
33             return value1 == value2;
34         }
35 
36         public static bool AreEqual(double value1, double value2)
37         {
38             return value1 == value2;
39         }
40     }
41 }

上述代码的问题是我们在每个方法体内重复同样的逻辑代码。然而,如果明天我们需要比较两个float或者long数据类型,那需要创建更多的函数体。

如何解决上述问题呢?

我们可以用泛型来解决以上问题。用泛型,可以使AreEqual()函数与不同的数据类型一起工作。我们先用泛型修改一下代码,然后再讨论泛型如何定义与工作。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 
 6 namespace Genrics
 7 {
 8     class Program
 9     {
10         static void Main(string[] args)
11         {
12             bool IsEqual = ClsCalculator.AreEqual("ABC","ABC");
13             if (IsEqual)
14             {
15                 Console.WriteLine("Both is Equal.");
16             }
17             else
18              {
19                 Console.WriteLine("Both are Not Equal.");
20              }
21             Console.ReadKey();
22         }
23 
24     }
25     
26 
27     public class ClsCalculator
28     { 
29         //Now this method can accept any data type
30         public static bool AreEqual<T>(T value1, T value2)
31         {
32             return value1.Equals(value2);
33         }
34     }
35 }

 

 

注:注重一下第12行代码,bool IsEqual = ClsCalculator.AreEqual("ABC","ABC");此行代码相对 于前面的代码没有任何变化;下面将代码按照泛型修改一下,再看一下运行结果有无变化,如下:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 
 6 namespace Genrics
 7 {
 8     class Program
 9     {
10         static void Main(string[] args)
11         {
12             bool IsEqual = ClsCalculator.AreEqual<string>("ABC","ABC");
13             if (IsEqual)
14             {
15                 Console.WriteLine("Both is Equal.");
16             }
17             else
18              {
19                 Console.WriteLine("Both are Not Equal.");
20              }
21             Console.ReadKey();
22         }
23 
24     }
25     
26 
27     public class ClsCalculator
28     { 
29         //Now this method can accept any data type
30         public static bool AreEqual<T>(T value1, T value2)
31         {
32             return value1.Equals(value2);
33         }
34     }
35 }

 

 可见 ,代码修改为泛型后:bool IsEqual = ClsCalculator.AreEqual<string>("ABC","ABC");程序运行结果没有任何变化。

上面示例的目的是使AreEqual()函数泛型化(泛型化是指只实现一个函数方法就可以操作多种数据类型),我们用尖括号<T>做为特定的类型参数T。

我们用T类型参数做为函数方法体的参数,如下面图片所示:

 这时,如果想要调用AreEqual()函数方法,需要在调用的函数方法中指定具体的数据类型。比如,你想要使用整数类型,那么如下图所示,用尖括号将指定的int数据类型括起来以调用AreEqual()函数方法。

AreEqual()泛型方法语法格式工作原理如下:

如果要使用string数据类型,需要像下面这样调用AreEqual()函数:

bool IsEqual= ClsCalculator.AreEqual<string>(“ABC”, “ABC”);

现在,应该就可以理解泛型的重要性了吧!

二、泛型是什么

C#2.0的时候引入泛型,泛型允许我们脱离具体的数据类型来定义类和函数方法,换名话说就是泛型允许我们用尖括号为类成员的数据类型创建类。在汇编时代,这些尖括号被一些具体的数据类型代替,在C#中,泛型被应用在以下方面:

1、接口

2、抽象类

3、类

4、方法

5、静态方法

6、属性

7、事件

8、委托

9、运算符重载

三、泛型的优点

1、泛型提升了代码重用的效率;

2、泛型是安全类型。如果我们尝试使用不同的数据类型,而不是指定的一种通用定义的数据类型,编译的时候会容易出错。

3、使用泛型,避免了装箱和拆箱的可能,可以得到更好地的性能表现。

四、如何使用泛型

用泛型构造、泛型成员变量、泛型属性和泛型方法创建泛型类,如下所示:

 1 using System;
 2 
 3 namespace GenericsDemo
 4 {
 5     //MyGenericClass is a Generic Class
 6     class MyGenericClass<T>
 7     {
 8         //Generic variable
 9         //The data type is generic
10         private T genericMemberVariable;
11 
12         //Generic Constructor
13         //Constructor accepts one parameter of Generic type
14         public MyGenericClass(T value)
15         {
16             genericMemberVariable = value;
17         }
18 
19         //Generic Method
20         //Method accepts one Generic type Parameter
21         //Method return type also Generic
22         public T genericMethod(T genericParameter)
23         {
24             Console.WriteLine("Parameter type: {0}, value: {1}", typeof(T).ToString(), genericParameter);
25             Console.WriteLine("Return type: {0}, value: {1}", typeof(T).ToString(), genericMemberVariable);
26             return genericMemberVariable;
27         }
28 
29         //Generic Property
30         //The data type is generic
31         public T genericProperty { get; set; }
32     }
33 }

上面的示例中,我们创建了泛型类MyGenericClass<T>,尖括号<>表示MyGenericClass类是泛型类,类的具体数据类型后面再定义。

 

当创建MyGenericClass类的实例的时候,我们会指定具体的数据类型,编译器会将那个数据类型赋值给T。

在下面的示例中,我们会使用int做为数据类型:

 1 class Program
 2 {
 3     static void Main()
 4     {
 5         MyGenericClass<int> integerGenericClass = new MyGenericClass<int>(10);
 6 
 7         int val = integerGenericClass.genericMethod(200);
 8         Console.ReadKey();
 9     }
10 }

运行程序,输出结果如下:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 
 6 namespace myGenricClass
 7 {
 8     class Program
 9     {
10         static void Main(string[] args)
11         {
12             MyGenericClass<int> integerGenericClass = new MyGenericClass<int>(10);
13             int Val = integerGenericClass.genericMethod(200);
14             Console.ReadKey();
15         }
16     }
17     class MyGenericClass<T>
18     {
19         private T genericMemberVariable;
20 
21         public MyGenericClass(T value)
22         {
23             genericMemberVariable = value;
24         }
25 
26         public T genericMethod(T genericParameter)
27         {
28             Console.WriteLine("Parameter type :{0},valle:{1}",typeof(T).ToString(),genericParameter);
29             Console.WriteLine("Return type :{0},valle:{1}", typeof(T).ToString(), genericMemberVariable);
30             return genericMemberVariable;
31         }
32 
33         public T genericProperty { get; set; }
34     }
35 }

 下面图表会显示编译器中int数据类型如何替代T

编译器会编译上述代码如下图所示:

在实例化的时候 ,我们可以根据需求使用任何数据类型。比如,我们需要用到string 数据类型,可以像下面代码所示进行类的实例化

 1 class Program
 2 {
 3     static void Main()
 4     {
 5         MyGenericClass<string> stringGenericClass = new MyGenericClass<string>("Hello Generic World");
 6         stringGenericClass.genericProperty = "This is a generic property example.";
 7         string result = stringGenericClass.genericMethod("Generic Parameter");
 8         Console.ReadKey();
 9     }
10 }

 

 

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 
 6 namespace myGenricClass
 7 {
 8     class Program
 9     {
10         static void Main(string[] args)
11         {
12             MyGenericClass<int> integerGenericClass = new MyGenericClass<int>(10);
13             int Val = integerGenericClass.genericMethod(200);
14             
15             Console.WriteLine("-------------------");
16             MyGenericClass<string> stringGenericClass = new MyGenericClass<string>("Hello Generic World!");
17             stringGenericClass.genericProperty = "This is a generic property example.";
18             string result = stringGenericClass.genericMethod("Generic Parameter");
19             Console.ReadKey();
20         }
21     }
22     class MyGenericClass<T>
23     {
24         private T genericMemberVariable;
25 
26         public MyGenericClass(T value)
27         {
28             genericMemberVariable = value;
29         }
30 
31         public T genericMethod(T genericParameter)
32         {
33             Console.WriteLine("Parameter type :{0},valle:{1}",typeof(T).ToString(),genericParameter);
34             Console.WriteLine("Return type :{0},valle:{1}", typeof(T).ToString(), genericMemberVariable);
35             return genericMemberVariable;
36         }
37 
38         public T genericProperty { get; set; }
39     }
40 }
完整代码

希望以上文章可以使泛型的概念有个初步理解。泛型在集合类中经常有用到(集合类属于System.Collections.Generic 命名空间)。

posted @ 2022-02-19 16:31  chenlight  阅读(388)  评论(0)    收藏  举报