zoukankan      html  css  js  c++  java
  • 读改善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;
            }
        }
  • 相关阅读:
    Combine 框架,从0到1 —— 4.在 Combine 中使用计时器
    Combine 框架,从0到1 —— 4.在 Combine 中使用通知
    Combine 框架,从0到1 —— 3.使用 Subscriber 控制发布速度
    Combine 框架,从0到1 —— 2.通过 ConnectablePublisher 控制何时发布
    使用 Swift Package Manager 集成依赖库
    iOS 高效灵活地配置可复用视图组件的主题
    构建个人博客网站(基于Python Flask)
    Swift dynamic关键字
    Swift @objcMembers
    仅用递归函数操作逆序一个栈(Swift 4)
  • 原文地址:https://www.cnblogs.com/sunchong/p/4652687.html
Copyright © 2011-2022 走看看