.Net Collection的一些理解——记录一次向实习生的答疑

公司最近进了个实习生,每天下班前我都会花一些时间来解答一下实习生的一些疑问。今天问起了关于集合排序方法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 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

.Net Collection的一些理解——记录一次向实习生的答疑

时间: 2024-10-10 09:28:43

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

金蝶handler中 collection 代码片段理解

1,AtsOverTimeBillBatchEditHandler中collection的理解 1 SelectorItemCollection selectors = new SelectorItemCollection(); 2 //自定义集合SelectorItemCollection的定义 3 selectors.add(new SelectorItemInfo("id")); //集合selectors中添加id 4 selectors.add(new SelectorIte

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

ASM:Automatic Storage Management,是ORACEL10G以后为了简化存储管理的复杂性,也是为了摆脱对其他厂商的依赖而推出的.ASM作为目前ORACLE推荐的首选存储方案,除了具有集群文件系统的功能外,同时还集成了冗余.IO分散等卷管理器的功能.我们可以通过两种方式使用ASM,包括建立在裸设备上,如果安装了ASMLib,也可以建立的块设备上. 使用ASM需要从ORACLE官方网站下载ASM驱动包,一般包括了ASM和ASMLib两部分,需要注意ASM和ASMLib是两部

关于闭包的理解记录

问题:如何从从外部读取函数局部变量? 解决方法:在函数的内部,再定义一个函数 function lazy_sum(arr) { var sum = function () { return arr.reduce(function (x, y) { return x + y; }); } return sum; } var f = lazy_sum([1, 2, 3, 4, 5]); f(); // 15 从js的作用域结构我们可以知道函数 lazy_sum可以读取sum中的局部变量和参数,那么只

JavaScript的理解记录(5)

---接上篇: 三.DOM解析: 1.Document Object Model(DOM):是表示和操作HTML和XML文档内容的基础API;其中几个重要的类有:Document和Element,Text,这三个也是Node的其中几个子类: 2.文档元素的选取:返回值是Element或Element组成的数组NodeList; 通过ID :  document.getElementById('id');   通过name:      document.getElementsByName('nam

JavaScript的理解记录(4)

客户端JavaScript:客户端就是Web浏览器; 一. 前奏: Web文档(document):一些呈现静态信息的页面,虽然有的页面是会动的,但信息本身还是静态! Web应用:可以动态载入信息,相比与文档的文本化,应用看起来更加图形化: 客户端JavaScript的三个重要的对象:Window Document和Element:Window是全局对象,它的属性和方法是全局变量和全局函数,其中里面的属性location指向Location对象,Location对象指定当前窗口的URL:属性do

z-index 之理解 --记录(六)

1.z-index用来控制元素重叠时堆叠顺序.适用于:已经定位的元素(即position:relative/absolute/fixed). 2.不使用z-index的时候,堆叠顺序如下(从下到上): 根元素(即HTML元素)的background和borders 正常流中非定位后代元素(这些元素顺序按照HTML文档出现顺序) 已定位后代元素(这些元素顺序按照HTML文档出现顺序) 3.使用z-index的时候,堆叠顺序如下(从下到上): 根元素(即HTML元素)的背景和border 正常流中非

JavaScript的理解记录

1.JavaScript的数据类型分为两类:原始类型和对象类型: 原始类型包括:数字,字符串,布尔值,null和undefined; (都是不可变类型,值不可以修改) 对象类型:普通对象和函数: 函数可使用new新建对象,即构造函数:每个构造函数定义了一类对象--由构造函数初始化的对象组成的集合: 类可以看做是对象类型的子类型,javascript语言定义的类主要有:数组Array,函数Function,日期Date 正则RegExp: var pattern=/s$/ 等价于 var patt

学习和理解C#的委托

去年自学C#用的教程是入门级的<学通C#的24堂课>,教材里面也没有提到委托和事件,工作中也没怎么用到.后来一次在网上看了一些大牛的博客,读完之后感觉懵懵懂懂,似懂非懂,过了两三天之后,却又全然忘记了.毕竟学习这事,温故而知新,学了不用,自然忘得也很快.对于如我一样的初学者来说,较好地理解委托和事件并是一件容易的事.其实掌握了的人,会觉得也没什么,而没有掌握的人,每次见到委托和事件就会觉得很畏惧.前段时间看到张旭亮老师的博客中关于.NET 开发系列PPT中提到一个观点,没学会委托就等不会.NE

汇编实现时钟设置代码理解

下面的笔记是我在看<朱老师物联网大讲堂>(www.zhulaoshi.org)之后所做的笔记,只是大概根据自己看了视频与朱老师上课做的笔记而有的理解记录下来. 写了 有代码的,要把代码给理解完整. 朱老师的随堂程序是:clock.s // 时钟控制器基地址 #define ELFIN_CLOCK_POWER_BASE0xE0100000 // 时钟相关的寄存器相对时钟控制器基地址的偏移值 #define APLL_LOCK_OFFSET0x00 #define MPLL_LOCK_OFFSET