目录:

  • 建议10:创建对象时需要考虑是否实现比较器
  • 建议11:区别对待==与Equals
  • 建议12:重写Equals时也要重写GetHashCode

一、建议10:创建对象时需要考虑是否实现比较器

比较一下基本工资:

 class Salary : IComparable
{
public string Name { get; set; }
public decimal BaseSalary { get; set; }
public decimal Bonus { get; set; }
public int CompareTo(object obj)
{
Salary comparer = obj as Salary; if (BaseSalary > comparer.BaseSalary)
{
return ;
}
else if (BaseSalary == comparer.BaseSalary)
{
return ;
}
else
{
return -;
}
}
}

客户端调用:

 List<Salary> salaries = new List<Salary>();
salaries.Add(new Salary() { Name = "Sun", BaseSalary = });
salaries.Add(new Salary() { Name = "Yuan", BaseSalary = });
salaries.Add(new Salary() { Name = "Kun", BaseSalary = });
salaries.Add(new Salary() { Name = "Qun", BaseSalary = });
salaries.Add(new Salary() { Name = "Sun", BaseSalary = }); salaries.Sort(); foreach (var s in salaries)
{
Console.WriteLine("【Name】:{0},【BaseSalary】:¥{1}{2}", s.Name, s.BaseSalary,System.Environment.NewLine);
} Console.ReadKey();

运行:

如果不想用基本工资BaseSalary进行排序,而是以奖金Bonus进行排序,使用IComparer实现自定义比较器:

class BonusComparer : IComparer<Salary>
{
public int Compare(Salary x, Sarlary y)
{ return left.Bonus.CompareTo(right.Bonus);
}
}

客户端提供我们上面创建的比较器:

            List<Salary> salaries = new List<Salary>();
salaries.Add(new Salary() { Name = "Sun", BaseSalary = ,Bonus= });
salaries.Add(new Salary() { Name = "Yuan", BaseSalary = , Bonus = });
salaries.Add(new Salary() { Name = "Kun", BaseSalary = , Bonus = });
salaries.Add(new Salary() { Name = "Qun", BaseSalary = ,Bonus= });
salaries.Add(new Salary() { Name = "Dun", BaseSalary = ,Bonus= }); salaries.Sort(new BonusComparer()); foreach (var s in salaries)
{
Console.WriteLine("Name:【{0}】,BaseSalary:¥{1},Bonus:{2}{3}", s.Name, s.BaseSalary,s.Bonus, System.Environment.NewLine);
} Console.ReadKey();

输出:

二、建议11:区别对待==与Equals

两者都是指相等性,即:值相等性和引用相等性。

值类型:如果值类型相等,返回True。

引用类型:如果指向同一个引用,返回True。

很好理解,举个例子:

1、值类型:==与Equls()

            int x = ;

            int y = ;

            Console.WriteLine("int x=1;{0}int y=1;{0}", System.Environment.NewLine,System.Environment.NewLine);

            Console.WriteLine("x==y:{0}",x == y);

            Console.WriteLine("x.Equals(y):{0}{1}",x.Equals(y),System.Environment.NewLine);

            x = y;

            Console.WriteLine("x=y;{0}", System.Environment.NewLine);

            Console.WriteLine("x==y:{0}", x == y);

            Console.WriteLine("x.Equals(y):{0}", x.Equals(y));

            Console.ReadKey();

运行:

2、引用类型

class People
{
public String Name { get; set; }
}

客户端:

            People p1 = new People() { Name = "Sun" };

            People p2 = new People() { Name = "Yuan" };

            Console.WriteLine("People p1 = new People();{0}People p2 = new People();{1}", System.Environment.NewLine, System.Environment.NewLine);

            Console.WriteLine("p1==p2:{0}", p1 == p2);

            Console.WriteLine("p1.Equals(p2):{0}{1}", p1.Equals(p2), System.Environment.NewLine);

            Console.WriteLine("------------------------------------");

            p1 = p2;

            p1.Name = "Moon";

            Console.WriteLine("p1=p2;{0}", System.Environment.NewLine);

            Console.WriteLine("p1==p2:{0}", p1 == p2);

            Console.WriteLine("p1.Equals(p2):{0}", p1.Equals(p2));

运行:

后面我们修改了p1里Name="Moon"的值,但是,p2的Name值也变成了Moon。所以,==与Equal()在比较引用类型时,引用地址一样,返回True

3、引用类型重载Equals()达到值类型比较效果

还有一点,有时我们需要我们的类型看上去和string类型类似,有值类型的感觉。所以说,我们的这个引用类型,需要重载==或者Equals()。

这里建议只重载Equals()来达到像值类型一样的比较效果。保留==,保留引用比较。例如:生活中我们认为身份证号码一样的是同一个人。

 class People
{
public String Name { get; set; }
public string IDCode { get; set; } public override bool Equals(object obj)
{ People p = obj as People; return p.IDCode == IDCode;
}
}

客户端:

 People p1 = new People() { IDCode="No1" };

            People p2 = new People() { IDCode = "No1" };

            Console.WriteLine("People p1 = new People();{0}People p2 = new People();{1}", System.Environment.NewLine, System.Environment.NewLine);

            Console.WriteLine("p1.IDCode={0}", p1.IDCode);
Console.WriteLine("p2.IDCode={0}", p2.IDCode);
Console.WriteLine(); Console.WriteLine("p1==p2:{0}【保留引用地址的对比】", p1 == p2); Console.WriteLine("p1.Equals(p2):{0}【重载比较IDCode,值类型比较效果】{1}", p1.Equals(p2), System.Environment.NewLine); Console.WriteLine("----------------------------------"); p1 = p2; p1.IDCode = "No2"; Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("p1.IDCode={0}", p1.IDCode);
Console.WriteLine("p2.IDCode={0}", p2.IDCode);
Console.WriteLine(); Console.ForegroundColor = ConsoleColor.White; Console.WriteLine("p1=p2;{0}", System.Environment.NewLine); Console.WriteLine("p1==p2:{0}", p1 == p2); Console.WriteLine("p1.Equals(p2):{0}", p1.Equals(p2)); Console.ReadKey();

运行:

还有,Object.ReferenceEquals方法比较实例是否相同。验证引用的相等性。

三、建议12:重写Equals时也要重写GetHashCode

当我们重写Equals时,编译器会提示一条警告:

为什么会有这样的提示?

因为在 System.Collections.Hashtable类型和System.Collections .Generic.Dictionary类型以及一些其他的集合类,要求两个对象相等,必须具有相同的哈希码。

所以在重写Equals时,也应该重写GetashCode,确保相等性的算法和对象哈希码算法一致。

添加:添加一个新的键值对时,首先会获取对象的哈希码,这个哈希码指出键值对应该存在哪一个哈希桶中。

查询:查询集合的一个键时,也获取指定键对象的哈希码,这个哈希码指定了我们查找键值对所存在哪一个哈希桶中。所以我们就去哈希桶中搜索与当前指定的键对象的哈希码。

看一下下面这个实例:

class Program
{
static Dictionary<People, string> ppdic = new Dictionary<People, string>(); static void AddPP()
{
People p = new People(){Name="Sun"}; ppdic.Add(p, "Sun");

        //Console.WriteLine(p.GetHashCode());          
Console.WriteLine(ppdic.ContainsKey(p)); } static void Main(string[] args)
{
AddPP(); People pp = new People() { Name = "Sun" };         //Console.WriteLine(pp.GetHashCode()); Console.WriteLine(ppdic.ContainsKey(pp)); Console.ReadKey();
}
}
class People
{
public String Name { get; set; }
public string IDCode { get; set; } public override bool Equals(object obj)
{ People p = obj as People; return p.IDCode == IDCode;
}
}

这里,我们重写了People类的Equals方法,客户端中,首先调用了AddPP()方法,添加一个Name="Sun"的People对象.

紧跟着,我们也定义了同样一个Name="Sun"的People对象。因该说两次People对象一样,所以两次输出都应该为True.

(这里我们不重写Equals效果也是一样的,但这里的重点是说明:GetHashCode)

为什么相同的对象返回的结果不一样?其实上面说过了,CLR会为每个对象创建唯一的哈希码(在生存周期内),因为当前类没有重写GetHashCode方法,所以会调用Object的GetHashCode。

解开上面的两句注释,运行:我们看到两个实例对象(p、pp)的哈希码是不一样的。

如果我们定义的自定义类型会被用作字典等类型的Key值,那我们可能需要重写Equals的同时还要重写GetHashCode。以来正确地实现我们的需求。

 class People
{
public String Name { get; set; }
public string IDCode { get; set; } public override bool Equals(object obj)
{ People p = obj as People; return p.IDCode == IDCode;
} public override int GetHashCode()
{
if (IDCode != null)
return this.IDCode.GetHashCode();
return base.GetHashCode();
}
}

为了产生更好的哈希值的随机分布:

public override int GetHashCode()
{
if (IDCode != null)
return (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "$" + this.IDCode).GetHashCode();
return base.GetHashCode();
}

当然最后我们也最好实现继承类型安全接口:IEquatable<People>

我们的身份证IDCode设计为只读属性,实例化时跟随一个身份证。

最终版本:

 class People:IEquatable<People>
{ public People(string idCode)
{
this._idCode = idCode;
}
public String Name { get; set; } private string _idCode;
public string IDCode { get; private set; } public override bool Equals(object obj)
{ People p = obj as People; return p.IDCode == IDCode;
} public override int GetHashCode()
{
if (IDCode != null)
return (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "$" + this.IDCode).GetHashCode();
return base.GetHashCode();
} public bool Equals(People other)
{
return IDCode == other.IDCode;
}
}

读改善c#代码157个建议:建议10~12的更多相关文章

  1. 读改善c#代码157个建议:建议4~6

    目录: 建议4:TryParse比Parse好 建议5:使用int?确保值类型也可以为null 建议6:区别 readonly 和 const 的用法 一.建议4:TryParse比Parse 好 T ...

  2. 读改善c#代码157个建议:建议13~15

    目录: 建议13:为类型输出格式化字符串 建议14:正确实现浅拷贝和深拷贝 建议15:使用dynamic来简化反射实现 一.建议13:为类型输出格式化字符串 有些类型需要我们根据业务需求提供字符串的格 ...

  3. 读改善c#代码157个建议:建议7~9

    目录: 建议7:将0值作为枚举的默认值 建议8:避免给枚举类型的元素提供显示的值 建议9:习惯运算符重载 一.建议7:将0值作为枚举的默认值 允许使用的枚举类型有:byte.sbyte.short.u ...

  4. 读改善c#代码157个建议:建议1~3

    目录: 建议一:正确操作字符串 建议二:使用默认转型方法 建议三:区别对待强制转型和as 建议一.正确操作字符串 1.确保尽量少的装箱 static void Main(string[] args) ...

  5. 编写高质量代码改善程序的157个建议:第87个建议之区分WPF和WinForm的线程模型

    今天有时间了,继续<编写高质量代码改善程序的157个建议>的阅读,当我阅读到建议87的时候,里面的一些代码示例和文中所说的不一致了,是不是我现在用的是NetFramework 4.0的缘故 ...

  6. 编写高质量代码改善java程序的151个建议——导航开篇

    2014-05-16 09:08 by Jeff Li 前言 系列文章:[传送门] 下个星期度过这几天的奋战,会抓紧java的进阶学习.听过一句话,大哥说过,你一个月前的代码去看下,慘不忍睹是吧.确实 ...

  7. 编写高质量代码:改善Java程序的151个建议 --[117~128]

    编写高质量代码:改善Java程序的151个建议 --[117~128] Thread 不推荐覆写start方法 先看下Thread源码: public synchronized void start( ...

  8. Github即将破百万的PDF:编写高质量代码改善JAVA程序的151个建议

    在通往"Java技术殿堂"的路上,本书将为你指点迷津!内容全部由Java编码的最佳 实践组成,从语法.程序设计和架构.工具和框架.编码风格和编程思想等五大方面,对 Java程序员遇 ...

  9. 每周一书-编写高质量代码:改善C程序代码的125个建议

    首先说明,本周活动有效时间为2016年8月28日到2016年9月4日.本周为大家送出的书是由机械工业出版社出版,马伟编著的<编写高质量代码:改善C程序代码的125个建议>. 编辑推荐 10 ...

随机推荐

  1. 学了Java 你未必知道这些

    作为一个正奔跑向编程完美天堂的朝圣者,本人觉得在平常的编程中,应该要做到以下几点: 一:汝应注释,这样做既方便别人,也方便自己去读懂代码的逻辑 二:注重细节,为自己写的每行代码负责,比如,在并发编程的 ...

  2. Linux cp -a用法

    对于cp -a最主要的用法是在保留原文件属性的前提下复制文件.其实还有个很好的用法,如下: 大家知道linux下复制目录可以通过,cp -r dirname destdir 但是这样复制的目录属性会发 ...

  3. poj2761(treap入门)

    给n个数,然后m个询问,询问任意区间的第k小的数,特别的,任意两个区间不存在包含关系, 也就是说,将所有的询问按L排序之后, 对于i<j ,   Li < Lj 且 Ri < Rj ...

  4. usb host鼠标不能使用原因

    linux kernel 3.4.5的板子插入USB鼠标,出现例如以下错误LOG: [  191.177508] Plug in USB Port2 [  191.363516] usb 1-1: n ...

  5. 当执行游戏0xc000007b错误的解决方法

    如图,这个错误使无数玩家烦恼. 出现这个错误,可能是硬件的问题,也可能是软件的问题.可是,因为硬件引起该问题的概率非常小,而且除了更换硬件之外没有更好的解决方法,因此本文将具体介绍怎样通过软件解决此问 ...

  6. .net MVC AutoFac基地的环境建设

    在Nuget在运行安装引用 Install-Package Autofac -Version 3.1.0 Install-Package Autofac.Mvc4 public static void ...

  7. 行为驱动开发(BDD)

    行为驱动开发(BDD) 引言 BDD是对TDD理念的扩展.BDD强调有利害关系的技术团体和非技术团队都要参与到软件开发过程中.可以把它看成一种强调团体间合作的敏捷方法.大多数采用某种敏捷方法的团队最终 ...

  8. 远程连接到vultr vps的mysql服务器

    实验环境 vultr centos 6.7 x64 1. 首先要打开远程 vps的3306端口用于 mysql的连接 修改/etc/sysconfig/iptables 文件,添加3306端口的支持 ...

  9. 03.Msbuild

    MSBuild的深入认识 分类: 专题开发 自动化 2009-01-20 11:56 5711人阅读 评论(1) 收藏 举报 任务引擎脚本工作扩展build 最近在从事自动构造工作的过程中,对MSBu ...

  10. tcpdump VS tshark用法(转)

    Tcpdump是网络协议分析的基本工具.tshark是大名鼎鼎的开源网络协议分析工具wireshark (原名叫ethereal)的命令行版本,wireshark可对多达千余种网络协议进行解码分析.W ...