1,==,!=,<,>,<= and >= operators are comparison operators. The description of comparison operators in the Chinese version of C Language Specification 5.0 is as follows:
2. General Type System
3. Value Type Equal Function and Operator'='
3.1. Common types such as int, float, double, decimal, etc. inherit from ValueType, but they rewrite Equal inside the structure.
3.1.1, int, float, double, Equal function and'=='overloader function in decimal.
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;
}
//I don't know the internal code of this function for the time being. If you know it, please let me know.
//Based on the test results, it is assumed that if the two decimal numbers are equal, 0 will be returned.
[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. Interested can go Reference Source Look at all the code.
3.1.3. Test code:
//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);//Return true
Console.WriteLine(a.Equals(b));//Return true
Console.WriteLine(a.Equals((object)b));//Return true
/*
Console.WriteLine((object)a == b);//Compilation error: Operator'=='cannot be applied with'object' and'T'type operands
Console.WriteLine(a == (object)b);//Compilation error: Operator'=='cannot be applied with'object' and'T'type operands
//Console.WriteLine((object)a == (object)b);//Return to false, and explain why it is false. This is the reference type'==', which is described below.
*/
3.1.4. Conclusion: For simple common value types such as int, float, double, decimal, etc., the Equal function and operator'=', if its value is equal, return true; otherwise, return false.
3.2. Structural Structures
3.2.1. Equals functions within ValueType
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, Structures (only value type, rewriting Equal function and operator'=')
3.2.2.1. Test code:
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;
}
//The operator "Point.operator ==(Point, Point)" requires that the matching operator "!=" be defined as well.
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);//Return true
Console.WriteLine(p1.Equals(p2)); // Return true
Console.WriteLine(p1.Equals((object)p2)); // Return true
3.2.2.2. Conclusion: At this point, the program executes the Equal function and operator'==', which we rewrote.
3.2.3, Structures (Value type only, no override of Equal function and operator'=')
3.2.3.1. Test code:
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);//Compilation error: Operator "==" cannot be applied to operands of "Point" and "Point" types
Console.WriteLine(p1.Equals(p2)); // Return true
Console.WriteLine(p1.Equals((object)p2)); // Return true
p1.y = 100;
Console.WriteLine(p1.Equals(p2)); // Return false
Console.WriteLine(p1.Equals((object)p2)); // Return false
3.2.3.2. When the program executes, CanCompareBits(this) returns true, and the code executes return Fast Equals Check (this Obj, obj);
3.2.3.3. Conclusion: The program judges the values of all fields in struct and returns true if they are all equal; otherwise, false.
3.2.4, Complex Structures (Value Type, Reference Type, Rewriting Equal Function and Operator'=')
3.2.4.1. Test code:
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; // Value type members
public RefPoint rPoint; // Reference type members
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); //Return true
Console.WriteLine(p1.Equals(p2)); //Return true
Console.WriteLine(p1.Equals((object)p2)); //Return true
p2 = p1;
p2.vPoint = vPoint3;
Console.WriteLine(p1 == p2); //Return true
Console.WriteLine(p1.Equals(p2)); //Return false
Console.WriteLine(p1.Equals((object)p2)); //Return false
p2 = p1;
p2.rPoint = rPoint2;
Console.WriteLine(p1 == p2); //Return true
Console.WriteLine(p1.Equals(p2)); //Return true
Console.WriteLine(p1.Equals((object)p2)); //Return true
3.2.4.2. Conclusion: At this point, the program executes the Equal function and operator'==', which we rewrote.
3.2.5, Complex Structures (Internal Value Type, Reference Type, Not Rewriting Equal Function and Operator'=')
3.2.5.1. Test code:
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; // Value type members
public RefPoint rPoint; // Reference type members
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);//Compilation error: Operator "==" cannot be applied to operands of "Point" and "Point" types
p2.vPoint = vPoint2;
Console.WriteLine(p1.Equals(p2)); //Return true
Console.WriteLine(p1.Equals((object)p2)); //Return true
p2 = p1;
p2.vPoint = vPoint3;
Console.WriteLine(p1.Equals(p2)); //Return false
Console.WriteLine(p1.Equals((object)p2)); //Return false
p2 = p1;
p2.rPoint = rPoint2;
Console.WriteLine(p1.Equals(p2)); //Return false
Console.WriteLine(p1.Equals((object)p2)); //Return false
3.2.5.2. When the program executes, CanCompareBits(this) returns false, and the code executes the following statement of the ValueType class Equal function
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. Conclusion: The program judges all fields in struct, and the value type judges whether the value is equal; the reference type judges whether the reference is equal.
4. Reference Type Equal Function and Operator'='
4.1. String string
Introduction of String Equivalence Operators in Chinese Version 4.1.1 and C# Language Specification 5.0
4.1.2, Equal function of string and'=='overloaded operator function code
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. Reference Equals (this, value) If this and value are the same reference, return true; otherwise, return false.
{
string a = "a1!";
string b = "a1!";
Console.WriteLine(Object.ReferenceEquals(a, b));//Returning true, you can judge that the compiler optimizes the "a1!" pointed to by a and b into one place.
}
{
string a = "Test";
string b = string.Copy(a);
Console.WriteLine(Object.ReferenceEquals(a, b));//Return false
}
{
string a = "Test";
string b = (string)a.Clone();
Console.WriteLine(Object.ReferenceEquals(a, b));//Return true
}
{
char[] ch = new char[] { 'a', 'A', '@' };
string a = "aA@";
string b = new string(ch);
Console.WriteLine(Object.ReferenceEquals(a, b));//Return false
}
4.1.4 Before learning the EqualsHelper(String strA, String strB) function, let's look at a piece of code
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. Modified EqualsHelper(String strA, String strB) function
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])//Because I can't use m_firstChar, I modify it here by myself. ps: Personally, m_firstChar refers to the first character of a string, but it cannot be proved.
//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. Modification Notes
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 - > acquired two char values (low ASCII*65536 + high ASCII) [low ahead, high behind]. [Char two bytes, range U+0000 to U + FFFFFF]
3. (* (char*) a - > The data obtained is a char value [see the test example above]
4.1.7. Testing EqualsHelper(String strA, String strB) function
{
string a = "abcd";
string b = "abcd";
Console.WriteLine(EqualsHelper(a,b));//Return true
}
{
string a = "Test";
string b = string.Copy(a);
Console.WriteLine(EqualsHelper(a, b));//Return true
}
{
string a = "Test";
string b = (string)a.Clone();
Console.WriteLine(EqualsHelper(a, b));//Return true
}
{
char[] ch = new char[] { 'a', 'A', '@' };
string a = "aA@";
string b = new string(ch);
Console.WriteLine(EqualsHelper(a, b));//Return true
}
4.1.8. Conclusion: String type A = b, string.Equals(a, b), a.Equals(b), a.Equals((object)b), true if the value of a is the same as that of b; otherwise false.
4.2. class
Introduction of Reference Type Equal Operators in Chinese Version 4.2.1 and C# Language Specification 5.0
4.2.2. Equals function inside Object
Object
{
public virtual bool Equals(Object obj)
{
return RuntimeHelpers.Equals(this, obj);//Detailed code could not be found
}
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, Class (not rewriting Equal function and operator'=')
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));//Return true
Console.WriteLine(object.Equals(p1, p2));//Return true
Console.WriteLine(object.ReferenceEquals(p1, p2));//Return true
Console.WriteLine(p1 == p2);//Return true
p2 = new RefPoint(4, 5, 6);//Although the values are the same, the reference objects are different
Console.WriteLine(p1.Equals(p2));//Return false
Console.WriteLine(object.Equals(p1, p2));//Return false
Console.WriteLine(object.ReferenceEquals(p1, p2));//Return false
Console.WriteLine(p1 == p2);//Return false
4.2.4, Class (Rewriting Equal Function and Operator'=')
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));//Return true
Console.WriteLine(object.Equals(p1, p2));//Return true
Console.WriteLine(object.ReferenceEquals(p1, p2));//Return true
Console.WriteLine(p1 == p2);//Return true
p2 = new RefPoint(4, 50, 60);
Console.WriteLine(p1.Equals(p2));//Return true
Console.WriteLine(object.Equals(p1, p2));//Return true
Console.WriteLine(object.ReferenceEquals(p1, p2));//Return false
Console.WriteLine(p1 == p2);//Return true
4.2.5, Reference Equals (Object objA, Object objB) returns objA == objB. If objA and objB refer to the same object (it's useless to just decide whether to refer to the same object, even if we overload the'='operator ourselves), return true; otherwise, return false.
5. Summary
First, introduce the simple value type, then to the structure, string, class. Summarize the use of Equal and'=='for each type, deepen your memory, and hope to help you at the same time. Otherwise: This article only represents my point of view. If there is any mistake, please let me know.
6. Reference
6.2, C# Language Specification 5.0 Chinese Version