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 命名空间)。

浙公网安备 33010602011771号