在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
一、泛型 假设我要写个公用的输出传入参数的方法(不用泛型),因为万物皆对象的理由,我先定义一个方法show(object obj),如下面所示: public static void Show(object obj) { Console.WriteLine(obj.ToString()); } 执行这个方法 int i = 1; //装箱 Show(i); 如果传入的是值类型,值类型转换为引用类型,我们知道会发生装箱,这是对性能的损害,想想如果是个集合,就得多次执行装箱、拆箱操作。如ArrayList类,ArrayList储存对象,Add()方法定义为需要把一个对象作为参数,如果传入的值类型,就得装箱,在读取ArrayList中的值时,又得进行拆箱,如下面代码所示: var list = new ArrayList(); list.Add(1); //装箱 foreach (int i in list) { Console.WriteLine(i); //拆箱 } 如果使用泛型,就不会出现这样的问题了,我们使用List<T>类来改造上面代码: var list = new List<int>(); list.Add(1); foreach (int i in list) { Console.WriteLine(i); } 这里就不存在装箱和拆箱了,所以我们在使用集合的时候,尽量使用泛型集合,不要使用非泛型集合。 二、类型安全 在上面ArrayList类中,添加参数时,可以添加任何对象,比如上面的例子,如果在添加整数类型后再添加引用类型,这么做在编译时是没有任何问题,但是在foreach语句使用整数类型迭代的时候就会报错。 var list = new ArrayList(); list.Add(1); //装箱 list.Add("string"); foreach (int i in list) { Console.WriteLine(i); } 这时候就会报InvalidCastException的异常。 如果使用泛型集合List<T>的时候去重写上面的代码,在编译的时候就会报错。所以这个地方我们就能知道,泛型是在编译时就已经执行了,所以系统运行时我们时没有装箱拆箱的系统开销,而非泛型是在运行时执行的,所以可能导致异常发生; 三、创建泛型类和泛型方法 泛型方法,从我最先第一个例子Show(object) ,采用泛型来重写,定义为Show<T>(T); public static void Show<T>(T obj) { Console.WriteLine(obj.ToString()); } 泛型类,如public class List<T>{} 3.1 命名约定
public delegate void EventHandler<TEventArgs>(object sender,TEventArgs e); public delegate TOutput Convert<TInput,TOutput>(TInput input); public class SortedList<TKey,TValue>{}; 3.2 默认值 在泛型类和泛型方法中产生的一个问题是,在预先未知以下情况时,如何将默认值分配给参数化类型 T,给定参数化类型 T 的一个变量 t,只有当 T 为引用类型时,语句 t = null 才有效;只有当 T 为数值类型而不是结构时,语句 t = 0 才能正常使用。 解决方案是使用 default 关键字,此关键字对于引用类型会返回 null,对于数值类型会返回零。 对于结构,此关键字将返回初始化为零或 null 的每个结构成员。 使用方式如:T obj=default(T); 3.3 约束 在定义泛型类时,可以对客户端代码能够在实例化类时用于类型参数的类型种类施加限制。 如果客户端代码尝试使用某个约束所不允许的类型来实例化类,则会产生编译时错误。 这些限制称为约束。 约束是使用where上下文关键字指定的。 下表列出了六种类型的约束:
public class MyClass<T> where T : IComparer<T>, new() { } 上面代码,使用泛型类型添加了两个约束,声明指定类型T必须实现了IComparer接口,且必须有一个默认构造函数 public class MyClass<TOutput, TInput> where TOutput : IComparer<TOutput>, new() where TInput:class,TOutput { } 上面代码用了两个泛型类型,TOutput必须实现了IComparer接口,且必须有一个默认构造函数,TInput必须是引用类型,且类型必须是TOutput或派生自TOutput。 3.4 继承 泛型类型可以实现泛型接口,也可以派生自一个类。泛型类型可以派生自泛型基类,其要求必须重复接口的泛型类型,或者必须指定基类的类型。如下列所示: public class BaseClass<T> { } ///必须重复接口\基类的泛型类型 public class MyClass<T> : BaseClass<T> { } public class BaseClass<T> { } ///必须指定基类的类型 public class MyClass<T> : BaseClass<String> { } 派生类可以是泛型类或非泛型类,例如定义一个抽象的泛型基类,它在派生类中用一个具体的类型实现,如下列所示: public abstract class Calcu<T> { public abstract T Add(T x, T y); public abstract T Sub(T x, T y); } /// <summary> /// 派生类中具体的类型实现 /// </summary> public class IntCalcu : Calcu<int> { public override int Add(int x, int y) { return x + y; } public override int Sub(int x, int y) { return x - y; } } 四、结语 这些泛型类和泛型方法例如,通过使用泛型类型参数 T,您可以编写其他客户端代码能够使用的单个类,而不致引入运行时强制转换或装箱操作的成本或风险。在架构中有句话是让一切能延迟的延迟。
|
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论