c#中的泛型、委托、泛型委托、Action和Func及使用场景系列之一:泛型

本系列将分别介绍泛型和委托的概念,比较泛型和委托在使用场景上的本质不同点,

最后介绍泛型委托结合起来如何使用及.net自身提供的两个泛型委托Action和Func。

---------------------------------------------------------分隔符--------------------------------------------------------

泛型是C#2.0中推出的功能,目的是解决类型强制转换的效率和风险问题,泛型可以作用于接口、类、方法、事件和委托,

在使用泛型前,我们先看泛型是如何引入的,考虑下面的开发场景:向一个集合中添加元素,然后遍历输出元素,代码如下:

 1     public class Class1
 2     {
 3         public void Test()
 4         {
 5             ArrayList list = new ArrayList();
 6             list.Add("aaa");
 7             list.Add("bbb");
 8 
 9             for(int i=0;i<list.Count;i++)
10             {
11                 string str = (string)list[i];
12             }
13         } 
14         
15     }

因为ArrayList中的元素是object 类型的,所以用Add( )方法添加元素的时候有一个装箱(Boxing)的过程(将string转化为object),

这样在遍历使用的时候需要做拆箱(UnBoxing)的操作,即强制将object转化成string类型后再使用。

在装箱和拆箱的过程中就会损失性能和效率,而且弱类型对编程不友好,会带来意外的风险,换成泛型集合后代码如下:

 1         public void Test2()
 2         {
 3             List<string> list2 = new List<string>();
 4             list2.Add("aaa");
 5             list2.Add("bbb");
 6 
 7             for (int i = 0; i < list2.Count; i++)
 8             {
 9                 string str = list2[i];
10             }
11         }

声明集合类的时候就指明该集合中的元素只能接收字符串List<string>( ) ,遍历的时候也无需强制转换。

 

 场景二 : 根据传入的值比较大小并返回较大的那个值

 1     public class Class1
 2     {
 3         private int x1, y1;
 4         public Class1(int x, int y)
 5         {
 6             this.x1 = x;
 7             this.y1 = y;
 8         }
 9         public int GetBigger()
10         {
11             return x1 > y1 ? x1 : y1;
12         }
13          
14     }
 1     class Program
 2     {
 3         static void Main(string[] args)
 4         { 
 5 
 6             Class1 c1 = new Class1(3, 5);
 7             int i = c1.GetBigger();
 8             Console.WriteLine(i);
 9 10 
11             Console.ReadLine();
12         }
13     }

以上代码只能比较int型值,如果我们要比较两个浮点数的大小,Class1是不能复用的,必须再写Class2来满足这样的需求,

可以预见,Class1和Class2除了类型不同外,代码几乎一模一样,作为一个程序员我们不能给自己挖坑,

这里该泛型上场了,泛型特性提供了一种更优雅的方式,让多个类型共享一组代码,

我们用泛型来改造Class2,而不仅仅是对Class1做复制粘贴,将Class2写成一个泛型类代码如下:

 1     public class Class2<T> where T: IComparable 
 2     {
 3         private T x1, y1;
 4         public Class2(T x, T y)
 5         {
 6             this.x1 = x;
 7             this.y1 = y;
 8         }
 9         public T GetBigger()
10         {
11             return x1.CompareTo(y1)>0 ? x1 : y1;
12         }
13     }

在泛型类Class2中,我们在类名Class2后加了“<T>”这样一个泛型标记,T是一个类型占位符,用尖括号括起来放在类名后面,

有了这个声明后在这个类的内部就可以使用这个类型," where T: IComparable " 为泛型约束(where是关键字),

它告诉外部的调用者,传入的类型必须实现IComparable这个接口,否则编译器就会报错。因为类型 T 实现了IComparable接口,

所以GetBigger()方法中可以调用T类型的CompareTo()方法来比较两个数的大小(c#中所有的数值类型都实现了IComparable接口)。

调用代码如下(见红色代码部分):

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         { 
 5 
 6             Class1 c1 = new Class1(3, 5);
 7             int i = c1.GetBigger();
 8             Console.WriteLine(i);
 9 
10 
11             Class2<double> c2 = new Class2<double>(3.5, 5.2);
12             double i2 = c2.GetBigger();
13             Console.WriteLine(i2);
14 
15 
16             Console.ReadLine();
17         }
18     }

在调用的时候,可以明显看到Class1和Class2构造的不同,Class2后面多了" <double> " 这样一个泛型声明,

所以Class2构造函数传入的数必须是double型的,如果我们要比较整型数的大小,就可以直接复用Class2这个泛型类了,

代码如下(红色部分) :

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         { 
 5 
 6             Class1 c1 = new Class1(3,5);
 7             int i = c1.GetBigger();
 8             Console.WriteLine(i);
 9 
10 
11             Class2<double> c2 = new Class2<double>(3.5, 5.2);
12             double i2 = c2.GetBigger();
13             Console.WriteLine(i2);
14 
15 
16             Class2<int> c22 = new Class2<int>(99, 87);
17             int i22 = c22.GetBigger();
18             Console.WriteLine(i22);
19 
20 
21             Console.ReadLine();
22         }
23     }

只需在声明的时候传入int 类型就可以了,这样代码写起来就舒服多了。

--------------------------------------------------------------------分隔符---------------------------------------------------------------------------------

接下来我们看泛型方法的使用 :

明白了泛型类的使用,泛型方法的使用是类似的,定义泛型方法只需要在方法名后加上 "<T>"就可以了,如果有两个泛型类型,

就用逗号分隔两个泛型标识符,如 "<T1, T2>",多个泛型类型以此类推,我们将上面比较大小的功能用泛型方法来实现,代码如下:

1     public class Class3
2     {
3         public T GetBigger<T>(T x1, T y1) where T:IComparable
4         {
5             return    x1.CompareTo(y1) > 0 ? x1 : y1;
6         }
7     }

方法名GetBigger和方法参数之间加上泛型标记 "<T>" ,因为是比较两个数的大小,所以要对类型T用where做一下约束,

类型T必须实现IComparable接口(这个接口只有一个CompareTo( )方法), 这样类型变量 x1 才能调用CompareTo( ) 方法来比较大小。

调用的方式如下(见红色代码部分):

 1 class Program
 2     {
 3         static void Main(string[] args)
 4         { 
 5 
 6             Class1 c1 = new Class1(3,5);
 7             int i = c1.GetBigger();
 8             Console.WriteLine(i);
 9 
10 
11             Class2<double> c2 = new Class2<double>(3.5, 5.2);
12             double i2 = c2.GetBigger();
13             Console.WriteLine(i2);
14 
15 
16             Class2<int> c22 = new Class2<int>(99, 87);
17             int i22 = c22.GetBigger();
18             Console.WriteLine(i22);
19 
20 
21             Class3 c3 = new Class3();
22             int i3 = c3.GetBigger<int>(82, 37);
23             Console.WriteLine(i3);
24 
25 
26             Console.ReadLine();
27         }
28     }

如果要比较浮点数只需在调用GetBigger方法的时候将尖括号中的int替换成double就可以了(返回值i3的类型也要换成double),实现了代码的复用。

 

接下来一篇将介绍c#中的委托。

 

posted @ 2020-12-13 20:05  屏风马  阅读(876)  评论(0编辑  收藏  举报