1、==、!=、、= 運算符為比較運算符(comparison operator)。C 語言規範5.0中文版中比較運算符的描述如下: 2、通用類型系統 3、值類型Equal函數 and 運算符'==' 3.1、常見類型 int、float、double、decimal等雖然繼承自ValueType, ...
1、==、!=、<、>、<= 和>= 運算符為比較運算符(comparison operator)。C#語言規範5.0中文版中比較運算符的描述如下:
2、通用類型系統
3、值類型Equal函數 and 運算符'=='
3.1、常見類型 int、float、double、decimal等雖然繼承自ValueType,但其結構體內部重寫了Equal。
3.1.1、 int,float,double,decimal內部的Equal函數和 '=='重載符函數。
Int32
{
public override bool Equals(Object obj) {
if (!(obj is Int32)) {
return false;
}
return m_value == ((Int32)obj).m_value;
}
[System.Runtime.Versioning.NonVersionable]
public bool Equals(Int32 obj)
{
return m_value == obj;
}
}
Double
{
// True if obj is another Double with the same value as the current instance. This is
// a method of object equality, that only returns true if obj is also a double.
public override bool Equals(Object obj) {
if (!(obj is Double)) {
return false;
}
double temp = ((Double)obj).m_value;
// This code below is written this way for performance reasons i.e the != and == check is intentional.
if (temp == m_value) {
return true;
}
return IsNaN(temp) && IsNaN(m_value);
}
public bool Equals(Double obj)
{
if (obj == m_value) {
return true;
}
return IsNaN(obj) && IsNaN(m_value);
}
[System.Runtime.Versioning.NonVersionable]
public static bool operator ==(Double left, Double right) {
return left == right;
}
}
Single
{
public override bool Equals(Object obj) {
if (!(obj is Single)) {
return false;
}
float temp = ((Single)obj).m_value;
if (temp == m_value) {
return true;
}
return IsNaN(temp) && IsNaN(m_value);
}
public bool Equals(Single obj)
{
if (obj == m_value) {
return true;
}
return IsNaN(obj) && IsNaN(m_value);
}
[System.Runtime.Versioning.NonVersionable]
public static bool operator ==(Single left, Single right) {
return left == right;
}
}
Decimal
{
// Checks if this Decimal is equal to a given object. Returns true
// if the given object is a boxed Decimal and its value is equal to the
// value of this Decimal. Returns false otherwise.
//
[System.Security.SecuritySafeCritical] // auto-generated
public override bool Equals(Object value) {
if (value is Decimal) {
Decimal other = (Decimal)value;
return FCallCompare(ref this, ref other) == 0;
}
return false;
}
[System.Security.SecuritySafeCritical] // auto-generated
public bool Equals(Decimal value)
{
return FCallCompare(ref this, ref value) == 0;
}
[System.Security.SecuritySafeCritical] // auto-generated
public static bool operator ==(Decimal d1, Decimal d2) {
return FCallCompare(ref d1, ref d2) == 0;
}
//暫時不知道此函數內部代碼,如有知道還望告知。
//根據測試結果,推測如果兩個decimal數相等,返回0
[System.Security.SecurityCritical] // auto-generated
[ResourceExposure(ResourceScope.None)]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
private static extern int FCallCompare(ref Decimal d1, ref Decimal d2);
}
3.1.2、感興趣的可去Reference Source查看全部代碼。
3.1.3、測試代碼:
//T is int 、float、double、decimal、byte、char
T a = 1234567890;//0.1234567890f、0.123456789、1234567890M、(byte)11、'a'
T b = 1234567890;//0.1234567890f、0.123456789、1234567890M、(byte)11、'a'
Console.WriteLine(a == b);//返回true
Console.WriteLine(a.Equals(b));//返回true
Console.WriteLine(a.Equals((object)b));//返回true
/*
Console.WriteLine((object)a == b);//編譯錯誤:運算符‘==’無法應用與‘object’和‘T’類型操作數
Console.WriteLine(a == (object)b);//編譯錯誤:運算符‘==’無法應用與‘object’和‘T’類型操作數
//Console.WriteLine((object)a == (object)b);//返回false,下麵解釋為什麼是false。這個是引用類型'==',放到下文介紹
*/
3.1.4、結論:對於簡單常見值類型 int、float、double、decimal等,Equal函數 and 運算符'==',如果其值相等,返回true;否則,返回false。
3.2、 結構體struct
3.2.1、 ValueType內部的Equals函數
ValueType
{
[System.Security.SecuritySafeCritical]
public override bool Equals (Object obj) {
BCLDebug.Perf(false, "ValueType::Equals is not fast. "+this.GetType().FullName+" should override Equals(Object)");
if (null==obj) {
return false;
}
RuntimeType thisType = (RuntimeType)this.GetType();
RuntimeType thatType = (RuntimeType)obj.GetType();
if (thatType!=thisType) {
return false;
}
Object thisObj = (Object)this;
Object thisResult, thatResult;
// if there are no GC references in this object we can avoid reflection
// and do a fast memcmp
if (CanCompareBits(this))
return FastEqualsCheck(thisObj, obj);
FieldInfo[] thisFields = thisType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
for (int i=0; i<thisFields.Length; i++) {
thisResult = ((RtFieldInfo)thisFields[i]).UnsafeGetValue(thisObj);
thatResult = ((RtFieldInfo)thisFields[i]).UnsafeGetValue(obj);
if (thisResult == null) {
if (thatResult != null)
return false;
}
else
if (!thisResult.Equals(thatResult)) {
return false;
}
}
return true;
}
[System.Security.SecuritySafeCritical] // auto-generated
[ResourceExposure(ResourceScope.None)]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
private static extern bool CanCompareBits(Object obj);
[System.Security.SecuritySafeCritical] // auto-generated
[ResourceExposure(ResourceScope.None)]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
private static extern bool FastEqualsCheck(Object a, Object b);
}
3.2.2、結構體(只有值類型,重寫Equal函數 and 運算符'==')
3.2.2.1、測試代碼:
struct Point
{
public double x;
public double y;
public double z;
public Point(double X, double Y, double Z)
{
this.x = X;
this.y = Y;
this.z = Z;
}
public override bool Equals(Object obj)
{
if (!(obj is Point))
{
return false;
}
if (((Point)obj).x == this.x)
{
return true;
}
return false;
}
public bool Equals(Point obj)
{
if (obj.x == this.x)
{
return true;
}
return false;
}
//運算符“Point.operator ==(Point, Point)”要求也要定義匹配的運算符“!=”
public static bool operator ==(Point left, Point right)
{
return left.x == right.x;
}
public static bool operator !=(Point left, Point right)
{
return left.x != right.x;
}
}
Point p1 = new Point(1, 2, 3);
Point p2 = p1;
p1.y = 100;
Console.WriteLine(p1 == p2);//返回true
Console.WriteLine(p1.Equals(p2)); // 返回true
Console.WriteLine(p1.Equals((object)p2)); // 返回true
3.2.2.2、結論:此時程式執行我們重寫的Equal函數 and 運算符'=='。
3.2.3、結構體(只有值類型,不重寫Equal函數 and 運算符'==')
3.2.3.1、測試代碼:
struct Point
{
public double x;
public double y;
public double z;
public Point(double X, double Y, double Z)
{
this.x = X;
this.y = Y;
this.z = Z;
}
}
Point p1 = new Point(1, 2, 3);
Point p2 = p1;
Console.WriteLine(p1 == p2);//編譯錯誤:運算符"=="無法應用於"Point"和"Point"類型的操作數
Console.WriteLine(p1.Equals(p2)); // 返回true
Console.WriteLine(p1.Equals((object)p2)); // 返回true
p1.y = 100;
Console.WriteLine(p1.Equals(p2)); // 返回false
Console.WriteLine(p1.Equals((object)p2)); // 返回false
3.2.3.2、程式執行時,CanCompareBits(this)返回true,代碼執行return FastEqualsCheck(thisObj, obj);
3.2.3.3、結論:程式判斷struct裡面所有欄位的值,如果全部相等,返回true;否則,返回false。
3.2.4、複雜結構體(有值類型、引用類型,重寫Equal函數 and 運算符'==')
3.2.4.1、測試代碼:
public struct ValPoint
{
public double x;
public double y;
public double z;
public ValPoint(double X, double Y, double Z)
{
this.x = X;
this.y = Y;
this.z = Z;
}
public static bool operator ==(ValPoint left, ValPoint right)
{
return left.x == right.x;
}
public static bool operator !=(ValPoint left, ValPoint right)
{
return left.x != right.x;
}
}
public class RefPoint
{
public double x;
public double y;
public double z;
public RefPoint(double X, double Y, double Z)
{
this.x = X;
this.y = Y;
this.z = Z;
}
}
public struct ValLine
{
public ValPoint vPoint; // 值類型成員
public RefPoint rPoint; // 引用類型成員
public ValLine(ValPoint vPoint, RefPoint rPoint)
{
this.vPoint = vPoint;
this.rPoint = rPoint;
}
public override bool Equals(Object obj)
{
if (!(obj is ValLine))
{
return false;
}
if (((ValLine)obj).vPoint == this.vPoint)
{
return true;
}
return false;
}
public bool Equals(ValLine obj)
{
if (obj.vPoint == this.vPoint)
{
return true;
}
return false;
}
public static bool operator ==(ValLine left, ValLine right)
{
return left.vPoint == right.vPoint;
}
public static bool operator !=(ValLine left, ValLine right)
{
return left.vPoint != right.vPoint;
}
}
ValPoint vPoint = new ValPoint(1, 2, 3);
ValPoint vPoint2 = new ValPoint(1, 2, 3);
ValPoint vPoint3 = new ValPoint(10, 20, 30);
RefPoint rPoint = new RefPoint(4, 5, 6);
RefPoint rPoint2 = new RefPoint(7, 8, 9);
ValLine p1 = new ValLine(vPoint, rPoint);
ValLine p2 = p1;
p2.vPoint = vPoint2;
Console.WriteLine(p1 == p2); //返回true
Console.WriteLine(p1.Equals(p2)); //返回true
Console.WriteLine(p1.Equals((object)p2)); //返回true
p2 = p1;
p2.vPoint = vPoint3;
Console.WriteLine(p1 == p2); //返回true
Console.WriteLine(p1.Equals(p2)); //返回false
Console.WriteLine(p1.Equals((object)p2)); //返回false
p2 = p1;
p2.rPoint = rPoint2;
Console.WriteLine(p1 == p2); //返回true
Console.WriteLine(p1.Equals(p2)); //返回true
Console.WriteLine(p1.Equals((object)p2)); //返回true
3.2.4.2、結論:此時程式執行我們重寫的Equal函數 and 運算符'=='。
3.2.5、複雜結構體(內部值類型、引用類型,不重寫Equal函數 and 運算符'==')
3.2.5.1、測試代碼:
public struct ValPoint
{
public double x;
public double y;
public double z;
public ValPoint(double X, double Y, double Z)
{
this.x = X;
this.y = Y;
this.z = Z;
}
}
public class RefPoint
{
public double x;
public double y;
public double z;
public RefPoint(double X, double Y, double Z)
{
this.x = X;
this.y = Y;
this.z = Z;
}
}
public struct ValLine
{
public ValPoint vPoint; // 值類型成員
public RefPoint rPoint; // 引用類型成員
public ValLine(ValPoint vPoint, RefPoint rPoint)
{
this.vPoint = vPoint;
this.rPoint = rPoint;
}
}
ValPoint vPoint = new ValPoint(1, 2, 3);
ValPoint vPoint2 = new ValPoint(1, 2, 3);
ValPoint vPoint3 = new ValPoint(10, 20, 30);
RefPoint rPoint = new RefPoint(4, 5, 6);
RefPoint rPoint2 = new RefPoint(7, 8, 9);
ValLine p1 = new ValLine(vPoint, rPoint);
ValLine p2 = p1;
Console.WriteLine(p1 == p2);//編譯錯誤:運算符"=="無法應用於"Point"和"Point"類型的操作數
p2.vPoint = vPoint2;
Console.WriteLine(p1.Equals(p2)); //返回true
Console.WriteLine(p1.Equals((object)p2)); //返回true
p2 = p1;
p2.vPoint = vPoint3;
Console.WriteLine(p1.Equals(p2)); //返回false
Console.WriteLine(p1.Equals((object)p2)); //返回false
p2 = p1;
p2.rPoint = rPoint2;
Console.WriteLine(p1.Equals(p2)); //返回false
Console.WriteLine(p1.Equals((object)p2)); //返回false
3.2.5.2、程式執行時,CanCompareBits(this)返回false,代碼執行ValueType類Equal函數的下麵語句
FieldInfo[] thisFields = thisType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
for (int i=0; i<thisFields.Length; i++) {
thisResult = ((RtFieldInfo)thisFields[i]).UnsafeGetValue(thisObj);
thatResult = ((RtFieldInfo)thisFields[i]).UnsafeGetValue(obj);
if (thisResult == null) {
if (thatResult != null)
return false;
}
else
if (!thisResult.Equals(thatResult)) {
return false;
}
}
return true;
3.2.5.3、結論:程式判斷struct裡面所有欄位,值類型就判斷值是否相等;引用類型就判斷是否引用相等。
4、引用類型Equal函數 and 運算符'=='
4.1、字元串string
4.1.1、C#語言規範5.0中文版中的字元串相等運算符介紹
4.1.2、string的Equal函數和'=='重載運算符函數代碼
String
{
// Determines whether two strings match.
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public override bool Equals(Object obj) {
if (this == null) //this is necessary to guard against reverse-pinvokes and
throw new NullReferenceException(); //other callers who do not use the callvirt instruction
String str = obj as String;
if (str == null)
return false;
if (Object.ReferenceEquals(this, obj))
return true;
if (this.Length != str.Length)
return false;
return EqualsHelper(this, str);
}
// Determines whether two strings match.
[Pure]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public bool Equals(String value) {
if (this == null) //this is necessary to guard against reverse-pinvokes and
throw new NullReferenceException(); //other callers who do not use the callvirt instruction
if (value == null)
return false;
if (Object.ReferenceEquals(this, value))
return true;
if (this.Length != value.Length)
return false;
return EqualsHelper(this, value);
}
public static bool operator == (String a, String b) {
return String.Equals(a, b);
}
// Determines whether two Strings match.
[Pure]
public static bool Equals(String a, String b) {
if ((Object)a==(Object)b) {
return true;
}
if ((Object)a==null || (Object)b==null) {
return false;
}
if (a.Length != b.Length)
return false;
return EqualsHelper(a, b);
}
[System.Security.SecuritySafeCritical] // auto-generated
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
private unsafe static bool EqualsHelper(String strA, String strB)
{
Contract.Requires(strA != null);
Contract.Requires(strB != null);
Contract.Requires(strA.Length == strB.Length);
int length = strA.Length;
fixed (char* ap = &strA.m_firstChar) fixed (char* bp = &strB.m_firstChar)
{
char* a = ap;
char* b = bp;
// unroll the loop
#if AMD64
// for AMD64 bit platform we unroll by 12 and
// check 3 qword at a time. This is less code
// than the 32 bit case and is shorter
// pathlength
while (length >= 12)
{
if (*(long*)a != *(long*)b) return false;
if (*(long*)(a+4) != *(long*)(b+4)) return false;
if (*(long*)(a+8) != *(long*)(b+8)) return false;
a += 12; b += 12; length -= 12;
}
#else
while (length >= 10)
{
if (*(int*)a != *(int*)b) return false;
if (*(int*)(a+2) != *(int*)(b+2)) return false;
if (*(int*)(a+4) != *(int*)(b+4)) return false;
if (*(int*)(a+6) != *(int*)(b+6)) return false;
if (*(int*)(a+8) != *(int*)(b+8)) return false;
a += 10; b += 10; length -= 10;
}
#endif
// This depends on the fact that the String objects are
// always zero terminated and that the terminating zero is not included
// in the length. For odd string sizes, the last compare will include
// the zero terminator.
while (length > 0)
{
if (*(int*)a != *(int*)b) break;
a += 2; b += 2; length -= 2;
}
return (length <= 0);
}
}
}
4.1.3、Object.ReferenceEquals(this, value)如果this、value是同一個引用,返回true;否則,返回false。
{
string a = "a1!";
string b = "a1!";
Console.WriteLine(Object.ReferenceEquals(a, b));//返回true,可以判斷編譯器將a與b所指向的"a1!"優化成一個地方。
}
{
string a = "Test";
string b = string.Copy(a);
Console.WriteLine(Object.ReferenceEquals(a, b));//返回false
}
{
string a = "Test";
string b = (string)a.Clone();
Console.WriteLine(Object.ReferenceEquals(a, b));//返回true
}
{
char[] ch = new char[] { 'a', 'A', '@' };
string a = "aA@";
string b = new string(ch);
Console.WriteLine(Object.ReferenceEquals(a, b));//返回false
}
4.1.4、學習EqualsHelper(String strA, String strB)函數之前,我們先看一段代碼
unsafe
{
char[] firstCharA = "abc".ToCharArray();
int length = firstCharA.Length;
fixed (char* ap = firstCharA)
{
for (int i = 0; i < length; i++)
{
Console.WriteLine(*(char*)(ap + i));
}
}
}
unsafe
{
int[] firstCharA = new int[] { 1, 20, 300 };
int length = firstCharA.Length;
fixed (int* ap = firstCharA)
{
for (int i = 0; i < length; i++)
{
Console.WriteLine(*(int*)(ap + i));
}
}
}
4.1.5、修改後EqualsHelper(String strA, String strB)函數
private unsafe static bool EqualsHelper(String strA, String strB)
{
Contract.Requires(strA != null);
Contract.Requires(strB != null);
Contract.Requires(strA.Length == strB.Length);
int length = strA.Length;
char[] firstCharA = strA.ToCharArray();
char[] firstCharB = strB.ToCharArray();
fixed (char* ap = &firstCharA[0]) fixed (char* bp = &firstCharB[0])//因無法使用m_firstChar,此處是我自行修改。ps:個人認為m_firstChar是指字元串的第一字元,但是無法證明。
//fixed (char* ap = &strA.m_firstChar) fixed (char* bp = &strB.m_firstChar)
{
char* a = ap;
char* b = bp;
while (length >= 10)
{
if (*(int*)a != *(int*)b) return false;
if (*(int*)(a + 2) != *(int*)(b + 2)) return false;
if (*(int*)(a + 4) != *(int*)(b + 4)) return false;
if (*(int*)(a + 6) != *(int*)(b + 6)) return false;
if (*(int*)(a + 8) != *(int*)(b + 8)) return false;
a += 10; b += 10; length -= 10;
}
// This depends on the fact that the String objects are
// always zero terminated and that the terminating zero is not included
// in the length. For odd string sizes, the last compare will include
// the zero terminator.
while (length > 0)
{
if (*(int*)a != *(int*)b) break;
a += 2; b += 2; length -= 2;
}
return (length <= 0);
}
}
4.1.6、修改說明
1、fixed (char* ap = &strA.m_firstChar) fixed (char* bp = &strB.m_firstChar)-> fixed (char* ap = &firstCharA[0]) fixed (char* bp = &firstCharB[0])
2、(*(int*)a->獲取的數據是兩個char值(低位ASCII*65536+高位ASCII)[低位在前,高位在後]。 [char兩個位元組,範圍U+0000到U+FFFF]
3、(*(char*)a->獲取的數據是一個char值[見上面測試例子]
4.1.7、測試EqualsHelper(String strA, String strB)函數
{
string a = "abcd";
string b = "abcd";
Console.WriteLine(EqualsHelper(a,b));//返回true
}
{
string a = "Test";
string b = string.Copy(a);
Console.WriteLine(EqualsHelper(a, b));//返回true
}
{
string a = "Test";
string b = (string)a.Clone();
Console.WriteLine(EqualsHelper(a, b));//返回true
}
{
char[] ch = new char[] { 'a', 'A', '@' };
string a = "aA@";
string b = new string(ch);
Console.WriteLine(EqualsHelper(a, b));//返回true
}
4.1.8、結論:string類型 a == b、string.Equals(a, b)、a.Equals(b)、a.Equals((object)b),如果 a 的值與 b 的值相同,則為 true;否則為 false。
4.2、類class
4.2.1、C#語言規範5.0中文版中的引用類型相等運算符介紹
4.2.2、Object內部的Equals函數
Object
{
public virtual bool Equals(Object obj)
{
return RuntimeHelpers.Equals(this, obj);//無法查到詳細代碼
}
public static bool Equals(Object objA, Object objB)
{
if (objA==objB) {
return true;
}
if (objA==null || objB==null) {
return false;
}
return objA.Equals(objB);
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[System.Runtime.Versioning.NonVersionable]
public static bool ReferenceEquals (Object objA, Object objB) {
return objA == objB;
}
}
4.2.3、類(不重寫Equal函數 and 運算符'==')
public class RefPoint
{
public double x;
public double y;
public double z;
public RefPoint(double X, double Y, double Z)
{
this.x = X;
this.y = Y;
this.z = Z;
}
}
RefPoint p1 = new RefPoint(4, 5, 6);
RefPoint p2 = p1;
Console.WriteLine(p1.Equals(p2));//返回true
Console.WriteLine(object.Equals(p1, p2));//返回true
Console.WriteLine(object.ReferenceEquals(p1, p2));//返回true
Console.WriteLine(p1 == p2);//返回true
p2 = new RefPoint(4, 5, 6);//雖然值一樣,但是引用對象不一樣
Console.WriteLine(p1.Equals(p2));//返回false
Console.WriteLine(object.Equals(p1, p2));//返回false
Console.WriteLine(object.ReferenceEquals(p1, p2));//返回false
Console.WriteLine(p1 == p2);//返回false
4.2.4、類(重寫Equal函數 and 運算符'==')
public class RefPoint
{
public double x;
public double y;
public double z;
public RefPoint(double X, double Y, double Z)
{
this.x = X;
this.y = Y;
this.z = Z;
}
public override bool Equals(Object obj)
{
if (!(obj is RefPoint))
{
return false;
}
if (((RefPoint)obj).x == this.x)
{
return true;
}
return false;
}
public bool Equals(RefPoint obj)
{
if (obj.x == this.x)
{
return true;
}
return false;
}
public static bool operator ==(RefPoint left, RefPoint right)
{
return left.x == right.x;
}
public static bool operator !=(RefPoint left, RefPoint right)
{
return left.x != right.x;
}
}
RefPoint p1 = new RefPoint(4, 5, 6);
RefPoint p2 = p1;
Console.WriteLine(p1.Equals(p2));//返回true
Console.WriteLine(object.Equals(p1, p2));//返回true
Console.WriteLine(object.ReferenceEquals(p1, p2));//返回true
Console.WriteLine(p1 == p2);//返回true
p2 = new RefPoint(4, 50, 60);
Console.WriteLine(p1.Equals(p2));//返回true
Console.WriteLine(object.Equals(p1, p2));//返回true
Console.WriteLine(object.ReferenceEquals(p1, p2));//返回false
Console.WriteLine(p1 == p2);//返回true