• 读改善c#代码157个建议:建议10~12


    目录:

    • 建议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 1; } else if (BaseSalary == comparer.BaseSalary) { return 0; } else { return -1; } } }

     客户端调用:

     List<Salary> salaries = new List<Salary>();
                salaries.Add(new Salary() { Name = "Sun", BaseSalary = 1000 });
                salaries.Add(new Salary() { Name = "Yuan", BaseSalary = 2000 });
                salaries.Add(new Salary() { Name = "Kun", BaseSalary = 3000 });
                salaries.Add(new Salary() { Name = "Qun", BaseSalary = 3000 });
                salaries.Add(new Salary() { Name = "Sun", BaseSalary = 4000 });
    
                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 = 1000,Bonus=4000 });
                salaries.Add(new Salary() { Name = "Yuan", BaseSalary = 2000, Bonus = 3000 });
                salaries.Add(new Salary() { Name = "Kun", BaseSalary = 3000, Bonus = 2000 });
                salaries.Add(new Salary() { Name = "Qun", BaseSalary = 3000,Bonus=4000 });
                salaries.Add(new Salary() { Name = "Dun", BaseSalary = 4000,Bonus=0 });
    
                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 = 1;
    
                int y = 1;
    
                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;
            }
        }
  • 相关阅读:
    Linux之apt-get无sudo权限安装软件
    Java stream 并发应用案例
    java 执行 shell脚本通过mysql load data导入数据
    修改mysql存储过程或函数的定义着
    [ERR] 1118
    定时杀死mysql中sleep的进程
    centos7安装配置MariaDB10
    Tomcat设置JVM参数
    通Shell获取Tomcat进程号并杀死进程
    对于之前已经push的项目增加.gitignore配置文件不起作用的处理
  • 原文地址:https://www.cnblogs.com/sunchong/p/4652687.html
Copyright © 2020-2023  润新知