在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
从简单的例子理解泛型 复制代码 代码如下: //男演员实体类 public class Boy { //姓名 private string mName; //身高 private int mHeight; public string Name { get { return this.mName; } } public int Height { get { return this.mHeight; } } public Boy(string name, int height) { Console.WriteLine(new Compare().WhoIsBetter(boy1, boy2).Name); 代码很简单,Boy为男演员实体类,包含姓名和身高两个字段属性;Compare类中的WhoIsBetter为选拔逻辑方法,负责选出两个男演员中较高的那个;测试结果:刘德华完胜。 任何行业都是一样,需求变更无处不在。第二天,需要选女主角,导演说了,女演员,苗条是王道。于是代码变更,添加了女演员实体类,添加了女演员的选拔方法: 复制代码 代码如下: //添加女演员实体 public class Girl { //姓名 private string mName; //体重 private int mWeight; public string Name { get { return this.mName; } } public int Weight { get { return this.mWeight; } } public Girl(string name, int weight){ //女演员苗条是王道 Girl girl1 = new Girl("巩俐", 120); Console.WriteLine(new Compare().WhoIsBetter(boy1, boy2).Name); 结果选出了身高更高的刘德华,选出了体重更轻的周迅,导演很满意。但从程序设计角度,这段代码显然不够完美,第一天选男主角,第二天选女主角,往后还要选男配角,选女配角,选群众......按目前方式,只有往Compare类里不断添加方法才能满足导演需求,方法会越来越多,代码会越来越长。于是,我决定修改WhoIsBetter方法,让它以后可以支持男主,女主,男配,女配,男群众,女群众甚至支持所有两个对象之间的比较: 复制代码 代码如下: /// <summary> /// 男演员:实现IComparable接口 /// </summary> public class Boy : IComparable { //姓名 private string mName; //身高 private int mHeight; public string Name { get { return this.mName; } } public int Height { get { return this.mHeight; } } public Boy(string name, int height) { public int CompareTo(object obj) /// <summary> public Girl(string name, int weight){ public int CompareTo(object obj) 首先让实体类支持自定义的比较,男演员比较身高,女演员比较体重。自定义比较是通过实现IComparable接口完成的,在C#里但凡可以比较的类型,比如int、double、char等都实现了IComparable接口。关于IComparable接口此处不作详述,请读者自行查阅相关资料。 复制代码 代码如下: public class Compare { //万物皆object public object WhoIsBetter(object obj1, object obj2) { object result = obj2; //判断比较类型必须相同 if (obj1.GetType() == obj2.GetType()) { switch (obj1.GetType().ToString()) { //男演员选拔 case "Generic.Boy": if (((Boy)obj1).CompareTo(obj2) > 0) { result = obj1; } break; //女演员选拔 case "Generic.Girl": if (((Girl)obj1).CompareTo(obj2) > 0) { result = obj1; } break; //扩展int类型比较 case "System.Int32": if (((System.Int32)obj1).CompareTo(obj2) > 0) { result = obj1; } break; } } return result; } } 修改WhoIsBetter方法,除了支持对男演员、女演员的比较,为了展示其扩展性,还新增了int类型的比较。 复制代码 代码如下: //测试 static void Main(string[] args) { Boy boy1 = new Boy("潘长江", 165); Boy boy2 = new Boy("刘德华", 175); Girl girl1 = new Girl("巩俐", 120); Console.WriteLine(((Boy)new Compare().WhoIsBetter(boy1, boy2)).Name); Console.ReadLine(); 测试结果: 刘德华 周迅 175 120 OK,截止目前,似乎比较完美了,男演员比身高,女演员比体重,还支持int类型比大小,WhoIsBetter方法具有了重用性,如果有需要,往后还能扩展,拿来比较任意两个对象。在泛型出现以前,似乎确实比较完美,但这也只是相对的,我们来看看目前代码的弱点: 弱点1:方法的重用性 假设我们要让WhoIsBetter方法支持更多类型,比如支持基本的double,char,bool类型,支持以后导演可能提出的配角比较,群众比较,那么就必须不断的扩展方法内部代码,这带来极大的维护成本。 弱点2:类型安全问题 复制代码 代码如下: //测试 static void Main(string[] args) { Boy boy1 = new Boy("潘长江", 165); Boy boy2 = new Boy("刘德华", 175); Girl girl1 = new Girl("巩俐", 120); Console.WriteLine(((Boy)new Compare().WhoIsBetter(boy1, girl1)).Name); 如上代码我拿潘长江跟巩俐去比较。虽然万能的object给我们带来了便捷,同时也带来了风险,这段代码编译完全可以通过,但运行时会出现异常,girl对象是没法转换为Boy类型的,现实里去韩国可以变性,但代码里绝对不行。所以这个方法就像颗定时炸弹,一不小心传错了参数,就会导致严重后果,并且编译阶段完全不被发现。 弱点3:装箱拆箱导致的性能问题 当向WhoIsBetter方法中传递int参数时,object转换为int导致了拆箱操作: if (((System.Int32)obj1).CompareTo(obj2) > 0) 反编译获取MSIL: IL_0093: unbox.any [mscorlib]System.Int32 C#是强类型语言,但只要引用类型与值类型的相互转换,就避免不了Box与Unbox。有关装箱与拆箱的知识请读者自行查阅相关资料,此处不作详述。 理解泛型 OK,到泛型登场了,摘录了一段MSDN中对泛型的描述:泛型类和泛型方法同时具备可重用性、类型安全和效率,这是非泛型类和非泛型方法无法具备的。这三点,跟我们上面的例子相吻合。 看看使用泛型的解决方案: 复制代码 代码如下: public class Compare<T> where T : IComparable { public T WhoIsBetter(T t1, T t2) { if (t1.CompareTo(t2) > 0) { return t1; } else { return t2; } } } //测试 static void Main(string[] args) { Boy boy1 = new Boy("潘长江", 165); Boy boy2 = new Boy("刘德华", 175); Girl girl1 = new Girl("巩俐", 120); Console.WriteLine((new Compare<Boy>().WhoIsBetter(boy1, boy2)).Name); 这段代码在优雅度上完胜非泛型,并且可重用性大大提升,可以说它支持所有类型的比较,只要这个类型实现了IComparable接口,同时一劳永逸,不再需要在方法内部作任何扩展。 public class Compare<T> where T : IComparable{ //... } 泛型类的定义是在类名后面跟上<T>,这个是泛型专用语法,T表示传递进来的类型,你也可以用别的字母代替。 where T : IComparable ,从字面上就能理解,这段表示对T的类型约束。程序是遵循人的意志来执行的,按前面的例子,如果莫名其妙的让程序比较两个object,它没办法知道该去怎么比较。所以我们必须告诉程序,T必须是可比较的类型,T必须实现了IComparable接口。 关于泛型参数约束,MSDN提供了一张表格:
约束 说明 |
请发表评论