前言 Net开发基础系列文章对自己之前写过的代码备忘如能给人予帮助不甚荣幸个人能力有限如有差错或不足请及时指正 从简单的例子理解泛型 话说有家影视公司选拔偶像派男主角导演说了男演员身高是王道于是有下面代码 //男演员实体类 public class Boy { //姓名 private string mName; //身高 private int mHeight; public string Name { get { return thismName; } } public int Height { get { return thismHeight; } } public Boy(string name int height) { thismName = name; thismHeight = height; } } //演员选拔类 public class Compare { //导演导超女出生喜欢一对一PK public Boy WhoIsBetter(Boy boy Boy boy) { if (boyHeight > boyHeight) { return boy; } else { return boy; } } } //测试 static void Main(string[] args) { Boy boy = new Boy(潘长江 ) Boy boy = new Boy(刘德华 ) ConsoleWriteLine(new Compare()WhoIsBetter(boy boy)Name) ConsoleReadLine() } 代码很简单Boy为男演员实体类包含姓名和身高两个字段属性Compare类中的WhoIsBetter为选拔逻辑方法负责选出两个男演员中较高的那个测试结果刘德华完胜 任何行业都是一样需求变更无处不在第二天需要选女主角导演说了女演员苗条是王道于是代码变更添加了女演员实体类添加了女演员的选拔方法 //添加女演员实体 public class Girl { //姓名 private string mName; //体重 private int mWeight; public string Name { get { return thismName; } } public int Weight { get { return thismWeight; } } public Girl(string name int weight){ thismName = name; thismWeight = weight; } } //演员选拔类中添加一个女演员方法 public class Compare { //男演员身高是王道 public Boy WhoIsBetter(Boy boy Boy boy) { if (boyHeight > boyHeight) { return boy; } else { return boy; } } //女演员苗条是王道 public Girl WhoIsBetter(Girl girl Girl girl) { if (girlWeight < girlWeight) { return girl; } else { return girl; } } } //测试 static void Main(string[] args) { Boy boy = new Boy(潘长江 ) Boy boy = new Boy(刘德华 ) Girl girl = new Girl(巩俐 ) Girl girl = new Girl(周迅 ) ConsoleWriteLine(new Compare()WhoIsBetter(boy boy)Name) ConsoleWriteLine(new Compare()WhoIsBetter(girl girl)Name) ConsoleReadLine() } 结果选出了身高更高的刘德华选出了体重更轻的周迅导演很满意但从程序设计角度这段代码显然不够完美第一天选男主角第二天选女主角往后还要选男配角选女配角选群众……按目前方式只有往Compare类里不断添加方法才能满足导演需求方法会越来越多代码会越来越长于是我决定修改WhoIsBetter方法让它以后可以支持男主女主男配女配男群众女群众甚至支持所有两个对象之间的比较 /// <summary> /// 男演员实现IComparable接口 /// </summary> public class Boy : IComparable { //姓名 private string mName; //身高 private int mHeight; public string Name { get { return thismName; } } public int Height { get { return thismHeight; } } public Boy(string name int height) { thismName = name; thismHeight = height; } public int CompareTo(object obj) { //比较身高 return thismHeight ((Boy)obj)Height; } } /// <summary> /// 女演员实现IComparable接口 /// </summary> public class Girl : IComparable { //姓名 private string mName; //体重 private int mWeight; public string Name { get { return thismName; } } public int Weight { get { return thismWeight; } } public Girl(string name int weight){ thismName = name; thismWeight = weight; } public int CompareTo(object obj) { //比较体重 return ((Girl)obj)Weight thismWeight; } } 首先让实体类支持自定义的比较男演员比较身高女演员比较体重自定义比较是通过实现IComparable接口完成的在C#里但凡可以比较的类型比如intdoublechar等都实现了IComparable接口关于IComparable接口此处不作详述请读者自行查阅相关资料 public class Compare { //万物皆object public object WhoIsBetter(object obj object obj) { object result = obj; //判断比较类型必须相同 if (objGetType() == objGetType()) { switch (objGetType()ToString()) { //男演员选拔 case GenericBoy: if (((Boy)obj)CompareTo(obj) > ) { result = obj; } break; //女演员选拔 case GenericGirl: if (((Girl)obj)CompareTo(obj) > ) { result = obj; } break; //扩展int类型比较 case SystemInt: if (((SystemInt)obj)CompareTo(obj) > ) { result = obj; } break; } } return result; } } 修改WhoIsBetter方法除了支持对男演员女演员的比较为了展示其扩展性还新增了int类型的比较 //测试 static void Main(string[] args) { Boy boy = new Boy(潘长江 ) Boy boy = new Boy(刘德华 ) Girl girl = new Girl(巩俐 ) Girl girl = new Girl(周迅 ) ConsoleWriteLine(((Boy)new Compare()WhoIsBetter(boy boy))Name) ConsoleWriteLine(((Girl)new Compare()WhoIsBetter(girl girl))Name) ConsoleWriteLine(new Compare()WhoIsBetter(boyHeight boyHeight)) ConsoleWriteLine(new Compare()WhoIsBetter(girlWeight girlWeight)) ConsoleReadLine() } 测试结果 刘德华 周迅 OK截止目前似乎比较完美了男演员比身高女演员比体重还支持int类型比大小WhoIsBetter方法具有了重用性如果有需要往后还能扩展拿来比较任意两个对象在泛型出现以前似乎确实比较完美但这也只是相对的我们来看看目前代码的弱点 弱点:方法的重用性 假设我们要让WhoIsBetter方法支持更多类型比如支持基本的doublecharbool类型支持以后导演可能提出的配角比较群众比较那么就必须不断的扩展方法内部代码这带来极大的维护成本 弱点:类型安全问题 //测试 static void Main(string[] args) { Boy boy = new Boy(潘长江 ) Boy boy = new Boy(刘德华 ) Girl girl = new Girl(巩俐 ) Girl girl = new Girl(周迅 ) ConsoleWriteLine(((Boy)new Compare()WhoIsBetter(boy girl))Name) ConsoleReadLine() } 如上代码我拿潘长江跟巩俐去比较虽然万能的object给我们带来了便捷同时也带来了风险这段代码编译完全可以通过但运行时会出现异常girl对象是没法转换为Boy类型的现实里去韩国可以变性但代码里绝对不行所以这个方法就像颗定时炸弹一不小心传错了参数就会导致严重后果并且编译阶段完全不被发现 弱点:装箱拆箱导致的性能问题 当向WhoIsBetter方法中传递int参数时object转换为int导致了拆箱操作 if (((SystemInt)obj)CompareTo(obj) > ) 反编译获取MSIL: IL_: unboxany [mscorlib]SystemInt C#是强类型语言但只要引用类型与值类型的相互转换就避免不了Box与Unbox有关装箱与拆箱的知识请读者自行查阅相关资料此处不作详述 理解泛型 OK到泛型登场了摘录了一段MSDN中对泛型的描述泛型类和泛型方法同时具备可重用性类型安全和效率这是非泛型类和非泛型方法无法具备的这三点跟我们上面的例子相吻合 看看使用泛型的解决方案 public class Compare<T> where T : IComparable { public T WhoIsBetter(T t T t) { if (tCompareTo(t) > ) { return t; } else { return t; } } } //测试 static void Main(string[] args) { Boy boy = new Boy(潘长江 ) Boy boy = new Boy(刘德华 ) Girl girl = new Girl(巩俐 ) Girl girl = new Girl(周迅 ) ConsoleWriteLine((new Compare<Boy>()WhoIsBetter(boy boy))Name) ConsoleWriteLine((new Compare<Girl>()WhoIsBetter(girl girl))Name) ConsoleWriteLine(new Compare<int>()WhoIsBetter(boyHeight boyHeight)) ConsoleWriteLine(new Compare<string>()WhoIsBetter(boyName girlName)) ConsoleReadLine() } 这段代码在优雅度上完胜非泛型并且可重用性大大提升可以说它支持所有类型的比较只要这个类型实现了IComparable接口同时一劳永逸不再需要在方法内部作任何扩展 public class Compare<T> where T : IComparable{ //… } 泛型类的定义是在类名后面跟上<T>这个是泛型专用语法T表示传递进来的类型你也可以用别的字母代替 where T : IComparable 从字面上就能理解这段表示对T的类型约束程序是遵循人的意志来执行的按前面的例子如果莫名其妙的让程序比较两个object它没办法知道该去怎么比较所以我们必须告诉程序T必须是可比较的类型T必须实现了IComparable接口 关于泛型参数约束MSDN提供了一张表格 约束说明T结构类型参数必须是值类型可以指定除Nullable 以外的任何值类型T类类型参数必须是引用类型这一点也适用于任何类接口委托或数组类型Tnew()类型参数必须具有无参数的公共构造函数当与其他约束一起使用时new() 约束必须最后指定T<基类名>类型参数必须是指定的基类或派生自指定的基类T<接口名称>类型参数必须是指定的接口或实现指定的接口可以指定多个接口约束约束接口也可以是泛型的TU为T 提供的类型参数必须是为U 提供的参数或派生自为U 提供的参数DEMO下载 Visual Studio Net Framwork |