在.net中有幾種比較相等的方法
object的靜態ReferenceEquals()
object的靜態Equals()
object的樣本Equals()
運算子==
下面介紹一下他們的區別和用法。
ReferenceEquals
ReferenceEquals用於比較參考型別的引用是是否指向同一個對象。它只能比較參考型別。當把實值型別傳給它的時候永遠都會返回false,因為實值型別作為參數的時候首先會裝箱,經過裝箱的實值型別哪怕是指相等,但是也是兩個不同的對象,所以變數是指向不同的對象,所以永遠返回false。
int x = 10;
int y = 10;
bool b1 = object.ReferenceEquals(x,y);
這裡結果肯定是返回false,但是如果是比較參考型別,如果是兩個引用指向同一個對象,則為true。
我們還是先定義實體類
public class Person
{
private int _personId;
public int PersonId
{
get { return _personId; }
set { _personId = value; }
}
private string _firstName;
public string FirstName
{
get { return _firstName; }
set { _firstName = value; }
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set { _lastName = value; }
}
public Person() { }
public Person(int personId, string firstName, string lastName)
{
this._personId = personId;
this._firstName = firstName;
this._lastName = lastName;
}
}
調用代碼
Person person1 = new Person(1,"Edrick","Liu");
Person person2 = new Person(2, "Meci", "Luo");
Person person3 = person2;
bool br1= object.ReferenceEquals(person1,person2);
bool br2 = object.ReferenceEquals(person2, person3);
我們可以發現第一個返回false,第二個返回true。那麼如果其中有一個對象為null,或者兩個對象都為null呢?結果會為false,如果兩個都為null呢?結果為true。他們不會引發異常。
執行個體Equals
執行個體Equals算是比較複雜的一個比較方法。執行個體Equals可以比較引用是否指向同一個對象,同時可以按值來比較對象。如果要按值比較對象,我們就需要重載Equals對象來實現我們的比較邏輯。同時Equals預設也支援比較實值型別的相等。那麼我們該怎麼重載Equals來讓對象具有值相等性的比較呢?
MSDN給我們列出了一些準則
除涉及浮點型的情況外,x.Equals(x) 都返回 true。
x.Equals(y) 返回與 y.Equals(x) 相同的值。
如果 x 和 y 都為 NaN,則 x.Equals(y) 返回 true。
若且唯若 x.Equals(z) 返回 true 時,(x.Equals(y) && y.Equals(z)) 才返回 true。
只要不修改 x 和 y 引用的對象,對 x.Equals(y) 的相繼調用將返回相同的值。
x.Equals(nullNothingnullptrnull 引用(在 Visual Basic 中為 Nothing)) 返回 false。
我們來看看重寫代碼
public class Person
{
private int _personId;
public int PersonId
{
get { return _personId; }
set { _personId = value; }
}
private string _firstName;
public string FirstName
{
get { return _firstName; }
set { _firstName = value; }
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set { _lastName = value; }
}
public Person() { }
public Person(int personId, string firstName, string lastName)
{
this._personId = personId;
this._firstName = firstName;
this._lastName = lastName;
}
public override bool Equals(object obj)
{
if (obj != null && obj is Person)
{
Person p = obj as Person;
return (PersonId == p.PersonId) && (FirstName == p.FirstName) && (LastName == p.LastName);
}
else
{
return false;
}
}
public override int GetHashCode()
{
return base.GetHashCode()^PersonId;
}
}
調用代碼
Person person1 = new Person(1,"Edrick","Liu");
Person person2 = new Person(2, "Meci", "Luo");
Person person3 = person2;
Person person4 = new Person(1, "Edrick", "Liu");
Console.WriteLine(person4.Equals(person1));
Console.WriteLine(person4.Equals(person2));
我們可以看到結果,第一個為true,第二個為false。重載的時候不能出現異常。那麼如果有一個類繼承Person呢,我們又改如何比較衍生類別。
public class Student:Person
{
private int _studentNumber;
public int StudentNumber
{
get { return _studentNumber; }
set { _studentNumber = value; }
}
public Student() { }
public Student(int personId, string firstName, string lastName, int studentNumber)
{
this.PersonId = personId;
this.FirstName = firstName;
this.LastName = lastName;
this._studentNumber = studentNumber;
}
public override bool Equals(object obj)
{
if (obj != null && obj is Person)
{
Student s = obj as Student;
return base.Equals(obj)&&StudentNumber==s.StudentNumber;
}
else
{
return false;
}
}
public override int GetHashCode()
{
return base.GetHashCode()^StudentNumber;
}
}
調用代碼
Student s1 = new Student(1, "Edrick", "Liu", 1);
Student s2 = new Student(2, "Meci", "Luo", 2);
Student s3 = new Student(1, "Edrick", "Liu", 1);
Console.WriteLine(s1.Equals(s2));
Console.WriteLine(s1.Equals(s3));
我們只需要調用父類的Equals方法和比較衍生類別中的新值。
靜態Equals
這個方法算是比較有趣的一個方法了。這個方法也是靜態,它能比較引用,能比較實值型別。如果比較的類型重載了執行個體的Equals,那麼它也能也比較對象的值。所以它返回true有三種情況。
1,引用指向同一個對象
2,比較兩個null
3,重載了Equals的執行個體方法返回true
Student s1 = new Student(1, "Edrick", "Liu", 1);
Student s2 = new Student(2, "Meci", "Luo", 2);
Student s3 = new Student(1, "Edrick", "Liu", 1);
Student s4 = s3;
Console.WriteLine(object.Equals(s1,s3));
Console.WriteLine(object.Equals(s4, s3));
這兩個都為true,這裡靜態Equals跟靜態EqualsReference有一個區別,靜態Equals如果有一個參數為null會拋出異常。
下面討論一個有趣的現象,如果重載了Equals但是沒有重載==運算子,會發生什麼
Student s1 = new Student(1, "Edrick", "Liu", 1);
Student s2 = new Student(2, "Meci", "Luo", 2);
Student s3 = new Student(1, "Edrick", "Liu", 1);
Student s4 = s3;
Console.WriteLine(s1==s3);
Console.WriteLine(s3==s4);
第一個為false,第二個為true。這顯然不符合我們意圖,所以重載了Equals必須重載==,同樣重載了==也必須重載Equals。這樣符合我們的意圖,也能確保在使用集合的時候,代碼能按照我們的意圖工作。因為集合coll[0]==co[0]其實比較的是引用,但是如果我們的Equals比較的是對象的值那麼最後代碼還是不能按照我的期望的運行。
==運算子
==號運算子其實跟執行個體的Equals沒有多大的區別,==是運算子,而Equals是方法。他們都可以重寫。預設都能比較引用和比較值。關於==的重載可以參考運算子一文中的運算子多載。
總結他們的區別:
ReferenceEquals:靜態方法,不能重寫,只能比較引用,如果有一個參數為null會返回false,不會拋出異常,如果比較實值型別,則始終返回false。
Equals:執行個體方法,預設可以比較引用也可以比較值,可以重寫。可以按值比較對象。
靜態Equals:靜態方法,不能重寫。如果沒有重寫Equals,比較引用,或者比較值。如果重載了Equals方法。比較引用,或者比較值,或者按重寫的Equals比較,如果其中一個參數為null,拋出異常
==運算子:可以按引用比較,也可以按值比較。可以重寫。是操作運算子。
最後需要的是,如果重載了Equals,則最好是重載GetHashCode,必須重載==運算子。