比較物件相等性的四種方法
比較物件相等性的四種方法
System.Object定義了3個不同的方法,來比較物件的相等性:ReferenceEquals()和兩個版本的Equals()。再加上比較運算子(==),實際上是有四種比較相等的方式。
在程式設計中實際上我們只需要這兩種比較,c#中型別也就這兩種
(1)值型別的比較:一般我們就是判斷兩個值型別例項的各自包含的值是否相等
(2)引用型別的比較:由於引用型別在記憶體中的分佈有兩部分,一個是引用型別的引用(存在於執行緒棧中),一個是引用型別的值(存在於託管堆);所以我們比較引用型別也就存在兩種比較
預設情況下:值型別比較的是兩個值是否相等(不裝箱情況下),引用型別比較的是兩個引用是否相等。
1.ReferenceEquals()
ReferenceEquals()是一個靜態方法,測試兩個引用是否引用類的同一個例項。特別是兩個引用是否包含記憶體中的相同地址。作為靜態方法,他不能重寫,所以system.Object的實現程式碼保持不變。
2.虛方法Equals()
Equals()虛擬版本的System.Object實現程式碼也可以用於比較引用。但因為這個方法是虛方法,所以可以在自己的類中重寫他,從而按值來比較物件。
注意:對於Object物件比較的是引用!然而對於值型別,型別相同(不會進行型別自動轉換),並且數值相同(對於struct的每個成員都必須相同),則Equals返回 true,否則返回false。這是為什麼呢? 這是因為內建的值型別都重寫了Object.Equals方法,所以值型別的Equals方法與引用型別的Equals就產生了不同的效果。
Equals在程式執行時決定比較的型別--根據物件的實際型別進行比較,根據物件的型別呼叫他們各自的Equals虛方法。
3.靜態的Equals()方法
Equals()的靜態版本與虛擬例項版本的作用相同,其區別是靜態版本帶有兩個引數,並對他們進行相等性的比較。
用反編譯工具反編譯System.dll得到方法的實現原始碼:
public static bool Equals(object objA, object objB)
{
if (objA == objB)
{ return true; }
if ((objA != null) && (objB != null))
{ return objA.Equals(objB); }
return false;
}
4.比較運算子(==)
定義:靜態相等符號,對應存在的!=,這個符號是一個可以過載的二元操作符,可以用於比較兩個物件是否相等。使用==比較物件時,C#在編譯時就決定了所比較的型別,而且不會執行任何虛方法(Object.Equals)。這是大家所期望的相等行比較。
- 對於內建值型別,==判斷的是兩個物件的代數值是否相等。它會根據需要自動進行必要的型別轉換,並根據兩個物件的值是否相等返回true或者false
- 對於引用型別,則==一般情況下比較的這是引用型別的引用是否相等。
注意:但是某些內建的引用型別過載了==符號,例如string就過載==,使其比較的不是兩個字串的引用,而是比較的兩個字串字面量是否相等,所以對於引用型別最好不要使用==符號進行相等性比較,避免混淆。
【對於引用型別利用==除了string是比較其值外,其餘都是比較其引用,因為string是經常需要操作,所以會直接比較其值,所以會對其特殊對待,所以如果遇見特殊的引用型別需要檢視一下是否進行了==過載,預設情況大家都可以把==在比較引用型別時當成比較引用!】
下面是關於四種比較的例子:
class Program
{
static void Main(string[] args)
{
string personName1 = "張三";
string personName2 = "李四";
int age1 = 22;
int age2 = 22;
Student Person1, Person2;
Person1 = new Student(personName1);
Person2 = new Student(personName2);
//1.例項Equals() 虛方法,比較引用,可重寫來比較值
bool b1 = personName1.Equals(personName2);
bool b2 = age1.Equals(age2);
bool b3 = Person1.Equals(Person2);
Console.WriteLine("1.例項Equals()結果:");
Console.WriteLine("personName1.Equals(personName2) :{0}", b1);
Console.WriteLine("age1.Equals(age2) :{0}", b2);
Console.WriteLine("Person1.Equals(Person2) :{0}", b3);
Console.WriteLine("------------------------------------------------");
//2.==
bool b4 = personName1 == personName2;
bool b5 = age1 == age2;
bool b6 = Person1 == Person2;
Console.WriteLine("2.==:");
Console.WriteLine("personName1 == personName2 :{0}", b4);
Console.WriteLine("age1 == age2 :{0}", b5);
Console.WriteLine("Person1 == Person2 :{0}", b6);
Console.WriteLine("------------------------------------------------");
//3.ReferenceEquals()
bool b7 = ReferenceEquals(personName1, personName2);
bool b8 = ReferenceEquals(age1, age2);
bool b9 = ReferenceEquals(Person1, Person2);
Console.WriteLine("3.ReferenceEquals():");
Console.WriteLine("ReferenceEquals(personName1,personName2) :{0}", b7);
Console.WriteLine("ReferenceEquals(age1,age2) :{0}", b8);
Console.WriteLine("ReferenceEquals(Person1,Person2) :{0}", b9);
Console.WriteLine("------------------------------------------------");
//4.靜態的Equals()
bool b10 = Equals(personName1, personName2);
bool b11 = Equals(age1, age2);
bool b12 = Equals(Person1, Person2);
Console.WriteLine("3.Equals():");
Console.WriteLine("Equals(personName1,personName2) :{0}", b10);
Console.WriteLine("Equals(age1,age2) :{0}", b11);
Console.WriteLine("Equals(Person1,Person2) :{0}", b12);
Console.WriteLine("------------------------------------------------");
int i = 5;
float j = 5;
bool bb = i.Equals(j);//不會進行型別轉換
bool bb1 = i == j;
bool bb2 = Equals(i, j);
Console.WriteLine("i.Equals(j):{0}", bb);
Console.WriteLine("i==j:{0}", bb1);
Console.WriteLine("Equals(i,j):{0}", bb2);
Console.WriteLine();
Console.ReadKey();
}
}
public class Student
{
private string name;
public Student(string name)
{
this.name = name;
}
public void ShowName()
{
Console.WriteLine(this.name);
}
}
執行結果如下: