公司最近进了个实习生,每天下班前我都会花一些时间来解答一下实习生的一些疑问。今天问起了关于集合排序方法Sort的一些疑问,这让我一下回到自己刚刚入行的时候。那个时候也遇到了集合排序的问题,为发现接口IComparableICompare的妙处而兴奋,还在公司的内部分享会上分享了如何使用它们来排序。现在经过多年的开发实践以及学习,对于同一个问题又有了更加深入的理解。

一. 为什么说”实现了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方法是无法排序的,因为程序不知道我们对于自定义对象的排序规则, 这个时候就可以使用IComparableIComparer.

下面举一个实际的例子, 这里我们自定义了一个类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 ;
if (x.Year < y.Year)
return -;
return ;
}
}

在调用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

.Net Collection的一些理解——记录一次向实习生的答疑的更多相关文章

  1. javascript浅拷贝深拷贝理解记录

    javascript的深拷贝和浅拷贝问题几乎是面试必问的问题.好记性不如烂笔头,特此来记录一下自己对深拷贝浅拷贝的理解. 顾名思义,拷贝就是copy复制,在js中可以浅而理解为对一个对象或者数组的复制 ...

  2. 宽度搜索(BFS)中求最短路径问题理解记录

    借助ACM1242题深入理解迷宫类最短路径搜索并记录路径长度的问题及解决方法:这是初次接触优先队列,尤其是不知道该怎样去记忆在结构体重自定义大小比较的符号方向,很容易混淆符号向哪是从大到小排列,向哪是 ...

  3. 金蝶handler中 collection 代码片段理解

    1,AtsOverTimeBillBatchEditHandler中collection的理解 SelectorItemCollection selectors = new SelectorItemC ...

  4. 对ASM存储管理的一些初步理解记录

    ASM:Automatic Storage Management,是ORACEL10G以后为了简化存储管理的复杂性,也是为了摆脱对其他厂商的依赖而推出的.ASM作为目前ORACLE推荐的首选存储方案, ...

  5. FPGA时序约束理解记录

    最近整理了一下时序约束的内容,顺便发出来分享记录一下. 任何硬件想要工作正常,均需满足建立和保持时间,至于这个概念不再陈述. 下面将重点介绍两个概念:建立余量和保持余量.FPGA内部进行时序分析无非就 ...

  6. JavaScript的理解记录(6)

    ---接上篇: 四.CSS相关: 1.CSS不支持注释// 支持注释/* */ 2. 几种浏览器厂商前缀: Firefox : -moz-;    Chrome:-webkit- ;      IE: ...

  7. JavaScript的理解记录(5)

    ---接上篇: 三.DOM解析: 1.Document Object Model(DOM):是表示和操作HTML和XML文档内容的基础API;其中几个重要的类有:Document和Element,Te ...

  8. JavaScript的理解记录(4)

    客户端JavaScript:客户端就是Web浏览器; 一. 前奏: Web文档(document):一些呈现静态信息的页面,虽然有的页面是会动的,但信息本身还是静态! Web应用:可以动态载入信息,相 ...

  9. JavaScript的理解记录(3)

    ---接上篇 一.函数:    1. 函数定义后直接执行:var f = (function(x){ return x*10}(10)); 2. 函数的调用有四种方式: 作为函数:作为方法:作为构造函 ...

随机推荐

  1. 真正解决问题:maven eclipse tomcat java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener--转

    原文地址:http://www.cnblogs.com/amosli/p/4067665.html 在使用eclipse进行整合springmvc时经常会碰到这样的异常: java.lang.Clas ...

  2. objective-c 语法快速过(1)

    有一定 c++或者 java 基础,过一遍 oc 语法即可,都是相通的,个人认为难点是 oc 的内存管理,虽然有了 ARC,但是也需要学习下,因为有旧软件的维护. 建立在C语言的基础上,增加了一层小范 ...

  3. 【JavaWeb】MVC案例之新闻列表

    MVC案例之新闻列表 作者:白宁超 2016年6月6日15:26:30 摘要:本文主要针对javaweb基本开发之MVC案例的简单操作,里面涉及mysql数据库及表的创建,以及jsp页面和servle ...

  4. 记录一则ORA-00054,ORA-00031解决过程

    生产环境:AIX 5.3 + Oracle 10.2.0.5 任务要求:普通表改造分区表,历史数据不要   这个需求很简单: pl/sql导出建表语句,依次修改成分区的建表语句,注意将索引修改成本地索 ...

  5. Mongoose使用案例--让JSON数据直接入库MongoDB

    目录 1.准备工作. 2.配置Mongoose. 3.创建目录及文件. 4.插入数据,POST提交JSON增加一条记录. 5.查询数据,取出你插入数据库的记录. 一.准备工作 使用Express4创建 ...

  6. HTML中使用javascript解除禁止input输入框代码:

    转:表单中Readonly和Disabled的区别 参考资料: disabled和readonly区别: 参考博文1地址:http://blog.csdn.net/symgdwyh/article/d ...

  7. git版本回退, github版本回退

    上周提交了更改,过了周末回来说要撤销上个story.于是,需要找到上周提交的版本,rollback回来. git版本管理命令,自从习惯使用管理工具之后就很少接触了,当突然寻找其他指令的时候就成浆糊了, ...

  8. 【原创】基于日志增量,统计qps,并基于ip排序

    增量统计日志行数(只统计上一秒) dns_qps.py #!/usr/bin/env python #_*_coding:utf-8_*_ import datetime import re impo ...

  9. GO语言面向对象

    当初开发go语言的时候就是因为C++的特性太过于繁杂,从而使得很多C++的开发者因为C++的特性而头疼,go语言成功的精简了C++的特性,使其很简洁,很少的特性,却可以完成很多的事情. go语言中并没 ...

  10. UML类图简单介绍

    先上一张图: 概述 类图(Class Diagram)是面向对象系统建模中最常用和最重要的图,是定义其它图的基础.类图主要是用来显示系统中的类.接口以及它们之间的静态结构和关系的一种静态模型. 类图组 ...