.Net Collection的一些理解——记录一次向实习生的答疑
公司最近进了个实习生,每天下班前我都会花一些时间来解答一下实习生的一些疑问。今天问起了关于集合排序方法Sort的一些疑问,这让我一下回到自己刚刚入行的时候。那个时候也遇到了集合排序的问题,为发现接口IComparable和ICompare的妙处而兴奋,还在公司的内部分享会上分享了如何使用它们来排序。现在经过多年的开发实践以及学习,对于同一个问题又有了更加深入的理解。
一. 为什么说”实现了IEnumerable接口才能遍历”
实习生先是问了这个问题, 其实这个问题, 非常容易解答.
先来看看IEnumerable接口的定义:
public interface IEnumerable { IEnumerator GetEnumerator(); }
这个接口非常简单,主要就是一个方法GetEnumerator,用来返回一个IEnumerator对象。
继续深入下去,IEnumerator接口的定义如下:
public interface IEnumerator { bool MoveNext(); void Reset(); object Current { get; } }
上面的IEnumerator接口定义的属性和方法,只有一个目的,就是实现如何遍历。下面具体解释一下:
- Current属性, 用来返回当前集合项的值
- MoveNext方法, 移动到集合对象的下一项,如果有下一项,就返回true, 如果没有,就返回false.
- Reset(), 重置然后可以重头访问集合
到这里,为什么foreach能够遍历集合对象的原因就是因为集合对象都实现了IEnumerable接口,提供了具体的实现来遍历集合对象。
二. Sort()方法以及IComparable、IComparer接口
集合类型,都有Sort()排序方法,这个函数的一个重载版本中,需要提供一个IComparer类型的接口。为什么需要使用这个接口呢?这个和排序有什么关系?
想一想,实现排序必须的是什么?必须的是能够比较大小。对于int, string这种.Net内部类型,本身已经支持了IComparer, 也就是可以比较大小了。但是对于我们自己定义的类型,使用Sort方法是无法排序的,因为程序不知道我们对于自定义对象的排序规则, 这个时候就可以使用IComparable、IComparer.
下面举一个实际的例子, 这里我们自定义了一个类Car, 然后对Car的集合进行排序.
public class Car { public string Name { get; set; } public int Year { get; set; } public int Seats { get; set; } }
假如我们直接调用Sort方法, 就会悲剧抛出InvalidOperationException异常
2.1 IComparable接口
IComparable 接口提供了比较某个特定类型对象的方法. 这里来说, 我们要让Car实现IComparable 接口来达到比较Car对象的目的,从而实现排序。实现IComparable接口, 就必须实现CompareTo 方法, 具体如下:
public class Car: IComparable { public string Name { get; set; } public int Year { get; set; } public int Seats { get; set; } public int CompareTo(object obj) { var car = (Car) obj; return String.CompareOrdinal(Name, car.Name); } }
上面我们的Car对象实现了IComparable接口,按照Name字符串属性来比较. 直接运行Sort()方法,就能够得到排序结果:
2.2 IComparer接口
IComparer接口可以提供更加丰富和灵活的排序功能。 比如, 你可能需要在不同的场合,根据不同的属性来排序.
下面就来实现一个根据Car的Year属性来排序, 首先创建类CarYearComparer来对Car进行比较,比较是根据Year属性
public class CarYearComparer: IComparer<Car> { public int Compare(Car x, Car y) { if (x.Year > y.Year) return 1; if (x.Year < y.Year) return -1; return 0; } }
在调用Sort方法排序的时候,需要指定使用的IComparer实现:
cars.Sort(new CarYearComparer());
得到的排序结果和上面的根据Name属性不同:
还可以实现一个根据Car的属性Seats排序的IComparer. 所以使用IComparer更加的灵活
3. 面向对象设计思想
Collection中的Sort()方法很好地遵循了开放封闭原则,既很好地完成了排序的功能,同时又开放出了入口,让你可以为排序自定义规则。
下面是我个人的一些个人理解:
一个类应该做自己擅长的事情和核心的事情(单一职责),把不擅长的交给别人。但是这个”别人“是不是任何人都可以呢?当然不是,为了更好的限定别人,我们使用了接口限定。
当一个类依赖于抽象(也就是接口),那么这个类的适用范围就更加广,就能够更加超脱。老子一句”道可道,非常道;名可名,非常名”,正是没有具体化,所以可以解释万物。
更多的面向对象设计原则,参照http://www.360doc.com/content/11/0521/00/3554006_118258005.shtml
最后附上本文相关源代码: SortDemo
相关文章
- abp(net core)+easyui+efcore实现仓储管理系统——入库管理之十二(四十八)
- C# -- HttpWebRequest 和 HttpWebResponse 的使用 C#编写扫雷游戏 使用IIS调试ASP.NET网站程序 WCF入门教程 ASP.Net Core开发(踩坑)指南 ASP.Net Core Razor+AdminLTE 小试牛刀 webservice创建、部署和调用 .net接收post请求并把数据转为字典格式
- 第一节: Timer的定时任务的复习、Quartz.Net的入门使用、Aop思想的体现 第十九节: 结合【表达式目录树】来封装EF的BaseDal层的方法 第二十节: 深入理解并发机制以及解决方案(锁机制、EF自有机制、队列模式等) 框架搭建篇 第二十一节:ADO层次上的海量数据处理方案(SqlBulkCopy类插入和更新) 第十四节: 介绍四大并发集合类并结合单例模式下的队列来说明线程
- 线程安全使用(四) [.NET] 简单接入微信公众号开发:实现自动回复 [C#]C#中字符串的操作 自行实现比dotcore/dotnet更方便更高性能的对象二进制序列化 自已动手做高性能消息队列 自行实现高性能MVC WebAPI 面试题随笔 字符串反转
- MVC的验证(模型注解和非侵入式脚本的结合使用) .Net中初探Redis .net通过代码发送邮件 Log4net (Log for .net) 使用GDI技术创建ASP.NET验证码 Razor模板引擎 (RazorEngine) .Net程序员应该掌握的正则表达式
- asp.net mvc Html.BeginForm()及Html.Action用法
- 【ASP.NET MVC系列】浅谈数据注解和验证
- Word控件Spire.Doc 【页面背景】教程(2) ;C在 C#、VB.NET 中为 Word 添加页面边框
- 【.Net Core】服务生命周期和Vue生命周期
- .NET连接池的配置 【转】
- .NET 有用的第三方
- 对一个前端使用AngularJS后端使用ASP.NET Web API项目的理解(1)
- ASP.NET MVC3 在_ViewStart设置Layout使用RenderAction的注意事項
- 理解 ASP.NET Core: 处理管道
- 理解 ASP.NET Core: 认证
- asp.net 分析器错误消息: 文件.aspx.cs”不存在错误