C#泛型(Generic)
前言:
此系列都为个人对C#的回顾,属于个人理解,新司机可参考、求老司机指点。如果有什么问题或不同见解,欢迎大家与我沟通!
目录:
- 泛型是什么
通过“参数化类型(不指定类型)”来实现在同一份代码上操作多种数据类型。
在声明时不指定类型,编译后查看IL代码可看到生成为占位符“`1”(1表示只有一个泛型参数,两个就是`2,以此类推) ,调用时会生成不同类型的方法,也就是最终还是会生成多个方法!
- 泛型的好处及用途
我们来假设一个场景:需要在控制台输出各种类型(可能不太形象,个人感觉ORM框架中数据保存那块很形象)。
在泛型没出现之前,我们基本都是采用以下这种方式来实现:
这个时候该有人说了这样写以后扩展太麻烦,咱可以采用object,毕竟object是所有类型的基类,也就是以下这种:
确实,类型不明确的地方可以使用object类型,一样可以达到目的。不过这种办法会引起:
1.使用object导致的类型安全问题
2.拆箱装箱导致性能下降
泛型的出现就是为了解决以上几种情况的问题,上面那个例子可以改为:
综其上述,泛型的好处有:
1.泛型采用延迟声明思想,将“参数化类型”将类型抽象化,从而实现更为灵活的复用。
2.泛型赋予了代码更强的类型安全,更高的效率,更清晰的约束。
泛型的用途很广泛,在.net各处都有体现,比如常见的List<T>、IEnumerable<T>、ICollection<T>等等。个人觉得说起泛型就应该说说委托。。。
例如:Linq中的方法都是采用的泛型加委托
- 如何声明使用泛型
泛型类:
1 /// <summary> 2 /// 这就是一个泛型类,是不是很简单 3 /// </summary> 4 /// <typeparam name="T">类型参数</typeparam> 5 public class Generic<T> 6 { 7 /// <summary> 8 /// 泛型方法 9 /// </summary> 10 /// <param name="type">泛型参数,根据类指定</param> 11 /// <returns></returns> 12 public T OutPut(T type) 13 { 14 Console.WriteLine(type.GetType()); 15 return default(T); 16 } 17 } 18 19 public class Test 20 { 21 Generic<int> genericInt; 22 Generic<string> genericString; 23 public Test() 24 { 25 //泛型类调用1 26 genericInt = new Generic<int>(); 27 genericInt.OutPut(1); 28 //泛型类调用2 29 genericString = new Generic<string>(); 30 genericString.OutPut("我就是泛型方法,不过我的参数类型是根据类来决定的,我的兄弟会在下面粗现~~"); 31 } 32 }
泛型方法:
5 public class Generic<T> 6 { 7 /// <summary> 8 /// 泛型方法 9 /// </summary> 10 /// <param name="type">泛型参数,根据类指定</param> 11 /// <returns></returns> 12 public T OutPut(T type) 13 { 14 Console.WriteLine(type.GetType()); 15 return default(T);//default(T)返回类型默认值 16 } 17 18 /// <summary> 19 /// 泛型方法,注意看,我与上面不同哦!调用也不同哦! 20 /// </summary> 21 /// <typeparam name="TResult"></typeparam> 22 /// <param name="tresult"></param> 23 /// <returns></returns> 24 public TResult GenericAction<TResult>(TResult tresult) 25 { 26 Console.WriteLine(tresult.GetType()); 27 return default(TResult); 28 } 29 /// <summary> 30 /// 可以随便写,类上面也一样~~~~~ 31 /// </summary> 32 public TResult GenericAction<TResult, LResult, SResult>(TResult tresult,LResult lresult,SResult sresult) 33 { 34 Console.WriteLine(tresult.GetType()); 35 return default(TResult); 36 } 37 } 38 39 public class Test 40 { 41 Generic<int> genericInt; 42 Generic<string> genericString; 43 Generic<DateTime> genericDateTime; 44 public Test() 45 { 46 //泛型类调用1 47 genericInt = new Generic<int>(); 48 genericInt.OutPut(1); 49 //泛型类调用2 50 genericString = new Generic<string>(); 51 genericString.OutPut("我就是泛型方法,不过我的参数类型是根据类来决定的,我的兄弟会在下面粗现~~"); 52 //泛型方法调用3 53 genericDateTime = new Generic<DateTime>(); 54 genericDateTime.GenericAction<string>("我的最终输出结果是根据方法的类型参数决定的,跟类无关!"); 55 } 56 } 57 }
泛型接口:
1 //泛型接口 2 public interface IGeneric<T> 3 { 4 } 5 //普通类继承泛型接口,需要指定基类泛型类型 6 public class Generic : IGeneric<string> 7 { 8 } 9 //泛型类继承泛型接口 10 public class Test<T> : IGeneric<int> 11 { 12 } 13 //泛型类继承泛型接口,并运用子类的泛型类型 14 public class Test1<T> : IGeneric<T> 15 { 16 } 17 //随便怎么玩都可以~~~~~~~其他的大家都可以试试
泛型委托:个人觉得泛型委托是个重点,在.net中处处体现了这点,比如我上面所说到的Linq方法。如果有对委托不熟悉的,我会在后面写一篇关于对委托的介绍。
1.首先我们先自定义一个泛型委托
1 class Program 2 { 3 //这里定义了一个无参有返回值的泛型的委托 4 public delegate T CustomDelegate<T>(); 5 //这里定义了一个有参有返回值的泛型的委托 6 public delegate T CustomDelegate1<T>(T type); 7 8 static void Main(string[] args) 9 { 10 //声明这个无参有返回值泛型委托 11 CustomDelegate<int> customDelegate = new CustomDelegate<int>(() => { return 1; });//() => { return 1; }匿名方法 12 //调用这个泛型委托 13 customDelegate.Invoke(); 14 15 //声明这个有参有返回值泛型委托 16 CustomDelegate1<string> customDelegate1 = new CustomDelegate1<string>((i) => { return i; }); 17 customDelegate1.Invoke("有参有返回值"); 18 19 //其他的无参无返回值,有参无返回值大家都可以试下,也可以尝试定义成多个参数的委托试试!!! 20 } 21 }
2.微软在.net为我们封装好的三个泛型委托,为了简化咱们的工作量,不用自定义委托
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 //1.Func 6 Func<string> func = new Func<string>(() => { return default(string); }); 7 8 //2.Action 9 Action<string> action = new Action<string>((i) => { }); 10 11 //3.Predicate 12 Predicate<bool> pre = new Predicate<bool>((i) => { return true; }); 13 14 //这三个泛型委托用处不同. 15 //比如Func就在Linq方法中经常用到,在F12进去之后可以看到类型参数上带有out修饰符 16 //Action,在F12进去之后可以看到类型参数上带有in修饰符 17 //in 与 out 则就是我们后面要说的逆变与协变了 18 //Predicate,则就是一个条件判断委托了 19 //具体的应用场景大家可以想象下 20 } 21 }
泛型约束:
在定义泛型类时,可以对客户端代码能够在实例化类时用于类型参数的类型种类施加限制。如果客户端代码尝试使用某个约束所不允许的类型来实例化类,则会产生编译时错误。这些限制称为约束。约束是使用 where 上下文关键字指定的。下表列出了六种类型的约束:
约束 | 说明 |
---|---|
T:结构 |
类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型。有关更多信息,请参见使用可空类型(C# 编程指南)。 |
T:类 |
类型参数必须是引用类型,包括任何类、接口、委托或数组类型。 |
T:new() |
类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。 |
T:<基类名> |
类型参数必须是指定的基类或派生自指定的基类。 |
T:<接口名称> |
类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。 |
T:U |
为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。这称为裸类型约束。 |
1.T:结构
2.T:类
3.T:new()
4.T:<基类名>
5.T:<接口名称>
6.T:U
扩展:
让我们通过泛型与泛型委托来扩展一个IEnumerable方法(需要了解C#扩展方法)
1 public static class Extends //静态类 2 { 3 /// <summary> 4 /// 只要有一个满足于predicate条件就返回true 5 /// </summary> 6 /// <typeparam name="TSource"></typeparam> 7 /// <param name="tsource"></param> 8 /// <param name="predicate"></param> 9 /// <returns></returns> 10 public static bool MaxBool<TSource>(this IEnumerable<TSource> tsource, Func<TSource, bool> predicate) 11 { 12 foreach (var item in tsource) 13 { 14 if (predicate(item)) 15 { 16 return true; 17 } 18 } 19 return false; 20 } 21 } 22 23 24 static void Main(string[] args) 25 { 26 //为什么List能使用MaxBool()我就不用说了吧。。。。。 27 List<int> listA = new List<int>() { 1, 2, 3 }; 28 bool trueOrFalse = listA.MaxBool(item => item > 20); 29 }
这里只是一个简单的例子,大家可以试试对其他的进行扩展!