讀改善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;
        }
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM