# C# `==` vs `Equals()`: Khác nhau thế nào, khi nào dùng và những lỗi thường gặp
Trong C#, cả `==` và `Equals()` đều được dùng để so sánh hai giá trị hoặc hai object.
Ví dụ:
“`csharp
a == b
“`
và:
“`csharp
a.Equals(b)
“`
Nhìn qua có vẻ giống nhau, nhưng bản chất của chúng khác nhau:
– `==` là **operator**.
– `Equals()` là **method**.
– Với value type, cả hai thường so sánh giá trị.
– Với reference type, behavior phụ thuộc vào type đó có override hoặc overload equality hay không.
– `string` là trường hợp đặc biệt vì dù là reference type nhưng `==` được overload để so sánh nội dung.
– Nếu custom `Equals()`, cần quan tâm đến `GetHashCode()`.
– Nếu custom `==`, nên giữ behavior nhất quán với `Equals()`.
Đây là một chủ đề rất hay xuất hiện trong phỏng vấn .NET.
—
# 1. `==` là gì?
`==` là equality operator.
Ví dụ:
“`csharp
int a = 10;
int b = 10;
Console.WriteLine(a == b);
“`
Kết quả:
“`text
true
“`
Với các primitive value type như:
“`text
int
long
double
bool
char
“`
operator `==` thường so sánh giá trị.
Ví dụ:
“`csharp
int x = 10;
int y = 10;
x == y;
“`
thực chất đang hỏi:
“`text
10 có bằng 10 không?
“`
—
# 2. `Equals()` là gì?
`Equals()` là method được định nghĩa từ `System.Object`.
Mọi type trong C# đều kế thừa từ `object`, do đó đều có method:
“`csharp
Equals()
“`
Ví dụ:
“`csharp
int a = 10;
int b = 10;
Console.WriteLine(a.Equals(b));
“`
Kết quả:
“`text
true
“`
Với value type như `int`, `Equals()` so sánh value.
—
# 3. Reference Type làm mọi thứ phức tạp hơn
Giả sử có class:
“`csharp
public class User
{
public string Name { get; set; }
}
“`
Tạo hai object:
“`csharp
var user1 = new User
{
Name = “Vu”
};
var user2 = new User
{
Name = “Vu”
};
“`
Hai object có cùng dữ liệu.
Nhưng:
“`csharp
Console.WriteLine(user1 == user2);
“`
kết quả:
“`text
false
“`
Tại sao?
Vì mặc định đối với custom reference type chưa overload `==`, operator này so sánh reference.
Có thể hình dung:
“`text
user1
↓
Object A
Name = “Vu”
“`
và:
“`text
user2
↓
Object B
Name = “Vu”
“`
Data giống nhau nhưng đây là hai object khác nhau trong memory.
—
# 4. `Equals()` mặc định với reference type
Nếu tiếp tục:
“`csharp
Console.WriteLine(user1.Equals(user2));
“`
kết quả cũng thường là:
“`text
false
“`
nếu class `User` chưa override `Equals()`.
Implementation mặc định từ `object` đối với reference type về cơ bản kiểm tra reference identity.
Có thể hiểu:
“`text
user1 == user2
↓
different objects
↓
false
“`
và:
“`text
user1.Equals(user2)
↓
default object equality
↓
different objects
↓
false
“`
—
# 5. Override `Equals()` để định nghĩa Logical Equality
Giả sử business requirement của chúng ta là:
> Hai user được coi là giống nhau nếu có cùng Email.
Có thể override:
“`csharp
public class User
{
public string Email { get; set; }
public override bool Equals(object? obj)
{
if (obj is not User other)
return false;
return Email == other.Email;
}
public override int GetHashCode()
{
return Email.GetHashCode();
}
}
“`
Bây giờ:
“`csharp
var user1 = new User
{
Email = “vu@example.com”
};
var user2 = new User
{
Email = “vu@example.com”
};
“`
gọi:
“`csharp
user1.Equals(user2);
“`
sẽ trả:
“`text
true
“`
Dù:
“`text
user1 != user2 về mặt reference
“`
nhưng theo business logic:
“`text
Email giống nhau
“`
nên chúng được xem là logically equal.
—
# 6. Reference Equality và Value Equality
Đây là hai khái niệm rất quan trọng.
## Reference Equality
Hỏi:
> Hai biến có đang trỏ tới cùng một object không?
Ví dụ:
“`csharp
var user1 = new User();
var user2 = user1;
“`
Có thể hình dung:
“`text
user1 ──┐
├──> User Object
user2 ──┘
“`
Hai variable cùng reference.
—
## Value / Logical Equality
Hỏi:
> Hai object có được xem là giống nhau dựa trên dữ liệu/business meaning không?
Ví dụ:
“`text
User A:
Email = vu@example.com
User B:
Email = vu@example.com
“`
Dù là hai object khác nhau, chúng có thể được business coi là equal.
Đây là thứ chúng ta thường implement qua:
“`csharp
Equals()
“`
—
# 7. `ReferenceEquals()` là gì?
Nếu thực sự muốn kiểm tra hai biến có trỏ tới cùng một object không, có thể dùng:
“`csharp
object.ReferenceEquals(a, b);
“`
Ví dụ:
“`csharp
var user1 = new User();
var user2 = user1;
Console.WriteLine(
object.ReferenceEquals(user1, user2)
);
“`
Kết quả:
“`text
true
“`
Nhưng:
“`csharp
var user1 = new User();
var user2 = new User();
“`
thì:
“`csharp
object.ReferenceEquals(user1, user2);
“`
trả:
“`text
false
“`
—
# 8. `string` là trường hợp đặc biệt
`string` là reference type.
Nhưng thử:
“`csharp
string a = new string(
“hello”.ToCharArray()
);
string b = new string(
“hello”.ToCharArray()
);
“`
Đây là hai object string riêng biệt.
Nhưng:
“`csharp
Console.WriteLine(a == b);
“`
kết quả:
“`text
true
“`
Và:
“`csharp
Console.WriteLine(a.Equals(b));
“`
cũng:
“`text
true
“`
Tại sao?
Vì `string` đã overload operator:
“`csharp
==
“`
để so sánh nội dung.
—
# 9. Vì vậy không nên nói “`==` luôn so reference”
Đây là một câu trả lời phỏng vấn không chính xác.
Không nên nói:
> `==` checks reference and Equals checks value.
Điều này chỉ đúng trong một số trường hợp.
Chính xác hơn là:
> Behavior của `==` phụ thuộc vào implementation của type và operator overload.
Ví dụ:
“`text
int
“`
`==` so value.
“`text
string
“`
`==` so content.
“`text
custom class
“`
mặc định thường so reference.
“`text
record
“`
mặc định có value-based equality.
—
# 10. `==` có thể Overload
Custom class có thể định nghĩa lại operator:
“`csharp
public class User
{
public string Email { get; set; }
public static bool operator ==(
User? left,
User? right)
{
if (ReferenceEquals(left, right))
return true;
if (left is null || right is null)
return false;
return left.Email == right.Email;
}
public static bool operator !=(
User? left,
User? right)
{
return !(left == right);
}
}
“`
Bây giờ:
“`csharp
var user1 = new User
{
Email = “vu@example.com”
};
var user2 = new User
{
Email = “vu@example.com”
};
Console.WriteLine(user1 == user2);
“`
có thể trả:
“`text
true
“`
—
# 11. Nếu overload `==` thì nên overload `!=`
Một rule quan trọng trong C#:
Nếu custom:
“`csharp
==
“`
thì cũng nên định nghĩa:
“`csharp
!=
“`
Ví dụ:
“`csharp
public static bool operator !=(
User? left,
User? right)
{
return !(left == right);
}
“`
Nếu hai operator có behavior không nhất quán, code rất khó hiểu.
—
# 12. `Equals()` có thể Override
Khác với `==`, `Equals()` là virtual method.
Có thể override:
“`csharp
public override bool Equals(object? obj)
{
…
}
“`
Điều này cho phép class định nghĩa:
“`text
Logical Equality
“`
riêng.
—
# 13. Override Equals thì phải quan tâm GetHashCode
Đây là rule cực kỳ quan trọng:
> Nếu override `Equals()`, phải override `GetHashCode()`.
Ví dụ:
“`csharp
public class User
{
public string Email { get; init; }
public override bool Equals(object? obj)
{
return obj is User other
&& Email == other.Email;
}
public override int GetHashCode()
{
return Email.GetHashCode();
}
}
“`
Tại sao?
Vì các hash-based collection như:
“`text
Dictionary
HashSet
“`
sử dụng:
“`text
GetHashCode()
+
Equals()
“`
để xác định equality.
—
# 14. HashSet hoạt động như thế nào?
Giả sử:
“`csharp
var users = new HashSet
“`
Khi add:
“`csharp
users.Add(user);
“`
HashSet có thể làm conceptually:
“`text
GetHashCode()
↓
Find hash bucket
↓
Equals()
↓
Same object logically?
“`
Nếu:
“`text
Equals() = true
“`
thì hai object logically phải tạo hash code phù hợp với equality contract.
—
# 15. Equality Contract
Một rule quan trọng:
Nếu:
“`text
a.Equals(b) == true
“`
thì:
“`text
a.GetHashCode() == b.GetHashCode()
“`
phải đúng.
Nhưng chiều ngược lại không bắt buộc.
Hai object có thể có cùng hash code nhưng vẫn:
“`text
Equals() == false
“`
Đó gọi là:
“`text
Hash Collision
“`
—
# 16. Bug khi override Equals nhưng không override GetHashCode
Giả sử:
“`csharp
public override bool Equals(object? obj)
{
return obj is User other
&& Email == other.Email;
}
“`
nhưng không override:
“`csharp
GetHashCode()
“`
Hai object:
“`text
User A
Email = vu@example.com
User B
Email = vu@example.com
“`
có thể:
“`text
Equals = true
“`
nhưng:
“`text
HashCode khác nhau
“`
Khi cho vào:
“`csharp
HashSet
“`
có thể xảy ra behavior không đúng kỳ vọng.
—
# 17. Null Handling khác nhau
`==` thường rất tiện khi check null.
Ví dụ:
“`csharp
User? user = null;
if (user == null)
{
Console.WriteLine(“User is null”);
}
“`
Không có vấn đề gì.
Nhưng:
“`csharp
user.Equals(null);
“`
sẽ gây:
“`text
NullReferenceException
“`
vì:
“`text
user
“`
đang là null và bạn cố gọi instance method.
—
# 18. `object.Equals()` xử lý null an toàn hơn
Có static method:
“`csharp
object.Equals(a, b);
“`
Ví dụ:
“`csharp
object.Equals(null, null);
“`
trả:
“`text
true
“`
Và:
“`csharp
object.Equals(user1, user2);
“`
sẽ gọi equality logic phù hợp.
Điều này hữu ích khi object có thể null.
—
# 19. `ReferenceEquals()` khác `Equals()`
Ví dụ:
“`csharp
var user1 = new User
{
Email = “vu@example.com”
};
var user2 = new User
{
Email = “vu@example.com”
};
“`
Nếu `Equals()` đã override:
“`csharp
user1.Equals(user2);
“`
có thể:
“`text
true
“`
Nhưng:
“`csharp
ReferenceEquals(user1, user2);
“`
vẫn:
“`text
false
“`
Vì:
“`text
Logical equality
!=
Reference identity
“`
—
# 20. Record trong C#
C# `record` được thiết kế với value-based equality.
Ví dụ:
“`csharp
public record User(
string Name,
string Email
);
“`
Tạo:
“`csharp
var user1 = new User(
“Vu”,
“vu@example.com”
);
var user2 = new User(
“Vu”,
“vu@example.com”
);
“`
Sau đó:
“`csharp
user1 == user2;
“`
trả:
“`text
true
“`
Và:
“`csharp
user1.Equals(user2);
“`
cũng:
“`text
true
“`
Mặc dù chúng là hai object riêng biệt.
—
# 21. Class vs Record
Ví dụ class:
“`csharp
public class User
{
public string Name { get; init; }
}
“`
“`csharp
var a = new User { Name = “Vu” };
var b = new User { Name = “Vu” };
a == b;
“`
mặc định:
“`text
false
“`
Trong khi:
“`csharp
public record User(string Name);
“`
“`csharp
var a = new User(“Vu”);
var b = new User(“Vu”);
a == b;
“`
kết quả:
“`text
true
“`
Vì record mặc định sử dụng value-based equality.
—
# 22. Record phù hợp với Value Object
Ví dụ trong Domain-Driven Design:
“`csharp
public record Money(
decimal Amount,
string Currency
);
“`
Hai object:
“`csharp
new Money(100, “USD”)
“`
và:
“`csharp
new Money(100, “USD”)
“`
về business có thể được xem là cùng giá trị.
Do đó record rất phù hợp với:
“`text
DTO
Value Object
Immutable Data
Message
Command
Event
“`
—
# 23. `IEquatable<
Nếu muốn implement equality chuyên nghiệp hơn, đặc biệt với custom type, có thể implement:
“`csharp
IEquatable
“`
Ví dụ:
“`csharp
public class User : IEquatable
{
public string Email { get; init; } = string.Empty;
public bool Equals(User? other)
{
if (other is null)
return false;
return Email == other.Email;
}
public override bool Equals(object? obj)
{
return Equals(obj as User);
}
public override int GetHashCode()
{
return Email.GetHashCode();
}
}
“`
### Lợi ích của `IEquatable<
`IEquatable<
– Làm equality contract rõ ràng hơn.
– Tránh một số boxing hoặc type conversion không cần thiết.
– Hữu ích với generic collections.
– Cho phép custom type định nghĩa rõ thế nào là “equal”.
—
## 24. Khi nào nên implement `IEquatable<
`IEquatable<
– Thường xuyên được so sánh.
– Được sử dụng trong collections.
– Là Value Object.
– Có business equality rõ ràng.
– Được dùng trong Dictionary
– Được dùng trong HashSet
Ví dụ một số type phù hợp:
“`text
Money
EmailAddress
Coordinate
ProductCode
Version
DateRange
“`
—
## 25. Equality với `struct`
`struct` là value type.
Ví dụ:
“`csharp
public struct Point
{
public int X { get; set; }
public int Y { get; set; }
}
“`
Value type có equality behavior khác reference type.
Tuy nhiên, với custom struct, nếu cần performance tốt hoặc equality semantic rõ ràng thì vẫn nên cân nhắc implement:
“`csharp
IEquatable
“`
Ví dụ:
“`csharp
public readonly struct Point : IEquatable
{
public int X { get; }
public int Y { get; }
public Point(int x, int y)
{
X = x;
Y = y;
}
public bool Equals(Point other)
{
return X == other.X && Y == other.Y;
}
public override bool Equals(object? obj)
{
return obj is Point other && Equals(other);
}
public override int GetHashCode()
{
return HashCode.Combine(X, Y);
}
}
“`
Điều này đặc biệt hữu ích nếu struct được sử dụng nhiều trong:
“`text
Dictionary
HashSet
LINQ
Collections
“`
—
## 26. Một lỗi thiết kế phổ biến: Equality dựa trên mutable property
Ví dụ:
“`csharp
public class User
{
public string Email { get; set; } = string.Empty;
public override int GetHashCode()
{
return Email.GetHashCode();
}
}
“`
Sau đó thêm object vào `HashSet`:
“`csharp
var user = new User
{
Email = “old@example.com”
};
var users = new HashSet
{
user
};
“`
Tiếp theo thay đổi:
“`csharp
user.Email = “new@example.com”;
“`
Lúc này hash code của object đã thay đổi.
Nhưng object ban đầu đã được lưu trong bucket dựa trên hash code cũ.
Khi đó:
“`csharp
users.Contains(user);
“`
có thể hoạt động ngoài kỳ vọng.
—
## 27. Best Practice với Hash Equality
Nếu một property tham gia vào:
“`text
Equals()
+
GetHashCode()
“`
thì property đó tốt nhất nên:
“`text
Immutable
“`
hoặc ít nhất:
> Không nên thay đổi trong thời gian object đang nằm trong hash-based collection.
Ví dụ:
“`csharp
public class User
{
public string Email { get; init; } = string.Empty;
}
“`
sẽ an toàn hơn:
“`csharp
public string Email { get; set; }
“`
nếu `Email` được dùng để tính equality.
—
# 28. Entity Equality trong Domain Model
Trong Domain-Driven Design, một Entity thường có:
“`text
Identity
“`
Ví dụ:
“`csharp
public class User
{
public Guid Id { get; init; }
public string Name { get; set; } = string.Empty;
}
“`
Giả sử có hai user:
“`text
User A
Id = 111
Name = Vu
“`
và:
“`text
User B
Id = 222
Name = Vu
“`
Dù `Name` giống nhau, đây vẫn là hai user khác nhau.
Với Entity, equality thường dựa trên:
“`text
Identity
“`
chứ không phải tất cả property.
Ví dụ:
“`csharp
public bool Equals(User? other)
{
if (other is null)
return false;
return Id == other.Id;
}
“`
—
# 29. Value Object Equality
Value Object thì ngược lại.
Value Object thường không có identity độc lập.
Ví dụ:
“`text
Money
Amount = 100
Currency = USD
“`
Nếu hai object `Money` có:
“`text
Amount giống nhau
Currency giống nhau
“`
thì chúng thường được xem là equal.
Ví dụ:
“`csharp
public record Money(decimal Amount, string Currency);
“`
Sau đó:
“`csharp
var a = new Money(100, “USD”);
var b = new Money(100, “USD”);
Console.WriteLine(a == b);
“`
Kết quả:
“`text
true
“`
Đây là lý do `record` rất phù hợp cho Value Object.
—
# 30. `==` và Polymorphism
Một nuance quan trọng:
“`csharp
==
“`
là operator overload.
Trong khi:
“`csharp
Equals()
“`
là method có thể tham gia virtual dispatch.
Ví dụ:
“`csharp
public class Animal
{
public override bool Equals(object? obj)
{
…
}
}
“`
`Equals()` có thể được override.
Trong khi `==` được resolve dựa trên operator overload và compile-time type theo quy tắc overload resolution.
Vì vậy:
> `==` và `Equals()` không nên được xem là hoàn toàn giống nhau về cơ chế dispatch.
Đây là một lý do khiến custom equality phải được thiết kế cẩn thận.
—
# 31. Không nên custom equality nếu không có business reason
Không phải class nào cũng cần override:
“`csharp
Equals()
“`
hoặc:
“`csharp
GetHashCode()
“`
Ví dụ:
“`csharp
public class UserService
{
}
“`
Không có lý do gì để `UserService` cần custom equality.
Equality nên có semantic rõ ràng.
Một số ví dụ hợp lý:
“`text
Money
Coordinate
EmailAddress
ProductCode
Version
DateRange
“`
—
# 32. So sánh tổng quát
| Đặc điểm | `==` | `Equals()` | `ReferenceEquals()` |
|—|—|—|—|
| Loại | Operator | Method | Static Method |
| Có thể customize | Overload | Override | Không |
| Value Type | Thường value comparison | Value comparison | Không phù hợp để kiểm tra value equality |
| Custom Reference Type mặc định | Reference equality | Reference equality | Reference equality |
| `string` | Content comparison | Content comparison | Reference comparison |
| `record` | Value-based | Value-based | Reference comparison |
| Null handling | Thường tiện | Instance method cần chú ý `null` | Safe |
| Polymorphic | Operator overload resolution | Có virtual dispatch | Không |
—
# 33. Câu hỏi phỏng vấn: `==` và `Equals()` khác gì nhau?
Một câu trả lời tốt:
> `==` is an operator while `Equals()` is a virtual method defined on `System.Object`. The exact behavior depends on the type. For custom reference types, `==` normally compares references unless overloaded, while `Equals()` can be overridden to define logical equality. Types such as `string` and records provide value-based equality behavior.
Có thể trả lời ngắn hơn bằng tiếng Việt:
> `==` là operator, còn `Equals()` là method. Behavior của `==` phụ thuộc vào type và operator overload, còn `Equals()` có thể được override để định nghĩa logical equality.
—
# 34. Câu hỏi phỏng vấn: `==` có luôn so reference không?
Không.
Behavior của `==` phụ thuộc vào type.
Ví dụ:
“`csharp
string a = “hello”;
string b = “hello”;
Console.WriteLine(a == b);
“`
Kết quả:
“`text
true
“`
Mặc dù `string` là reference type.
Lý do:
> `System.String` overload equality operator để so sánh nội dung thay vì object reference.
Vì vậy không nên nhớ:
“`text
== luôn luôn so reference
“`
Đây là sai.
—
# 35. Câu hỏi phỏng vấn: Vì sao override `Equals()` phải override `GetHashCode()`?
Các collection như:
“`text
Dictionary
HashSet
“`
sử dụng:
“`text
GetHashCode()
“`
để xác định bucket.
Sau đó sử dụng:
“`text
Equals()
“`
để kiểm tra chính xác object.
Equality contract yêu cầu:
“`text
Nếu A.Equals(B) == true
“`
thì:
“`text
A.GetHashCode() == B.GetHashCode()
“`
phải đúng.
Ví dụ:
“`csharp
public override bool Equals(object? obj)
{
return obj is User other &&
Email == other.Email;
}
public override int GetHashCode()
{
return Email.GetHashCode();
}
“`
Nếu override `Equals()` nhưng không đảm bảo `GetHashCode()` nhất quán, hash-based collections có thể hoạt động sai.
—
# 36. Câu hỏi phỏng vấn: `ReferenceEquals()` dùng để làm gì?
`ReferenceEquals()` kiểm tra:
> Hai reference có đang trỏ tới đúng cùng một object instance hay không.
Ví dụ:
“`csharp
var a = new User();
var b = a;
Console.WriteLine(
ReferenceEquals(a, b)
);
“`
Kết quả:
“`text
true
“`
Nhưng:
“`csharp
var a = new User();
var b = new User();
Console.WriteLine(
ReferenceEquals(a, b)
);
“`
Kết quả:
“`text
false
“`
Ngay cả khi hai object có cùng dữ liệu.
—
# 37. Câu hỏi phỏng vấn: `string` là reference type, tại sao `==` lại trả về `true`?
Ví dụ:
“`csharp
string a = “hello”;
string b = “hello”;
Console.WriteLine(a == b);
“`
Kết quả:
“`text
true
“`
Câu trả lời:
> Because `System.String` overloads the equality operator to compare character values rather than object references.
Hay nói đơn giản:
> `string` đã overload `==` để so sánh nội dung.
—
# 38. Record khác Class ở Equality thế nào?
Một normal class:
“`csharp
public class User
{
public string Name { get; init; } = string.Empty;
}
“`
Ví dụ:
“`csharp
var a = new User
{
Name = “Vu”
};
var b = new User
{
Name = “Vu”
};
Console.WriteLine(a == b);
“`
Kết quả mặc định:
“`text
false
“`
Vì đây là hai object khác nhau.
Trong khi record:
“`csharp
public record User(string Name);
“`
Ví dụ:
“`csharp
var a = new User(“Vu”);
var b = new User(“Vu”);
Console.WriteLine(a == b);
“`
Kết quả:
“`text
true
“`
Có thể nhớ:
> Normal class mặc định dùng reference equality, còn record cung cấp value-based equality mặc định.
—
# 39. Cách nhớ nhanh
## `==`
“`text
==
↓
Operator
↓
Behavior depends on type
↓
Can be overloaded
“`
## `Equals()`
“`text
Equals()
↓
Method
↓
Logical equality
↓
Can be overridden
“`
## `ReferenceEquals()`
“`text
ReferenceEquals()
↓
Same exact object?
“`
—
# 40. Ví dụ tổng hợp
“`csharp
public record User(
string Name,
string Email
);
var a = new User(
“Vu”,
“vu@example.com”
);
var b = new User(
“Vu”,
“vu@example.com”
);
“`
Kiểm tra:
“`csharp
a == b;
“`
Kết quả:
“`text
true
“`
Tiếp theo:
“`csharp
a.Equals(b);
“`
Kết quả:
“`text
true
“`
Nhưng:
“`csharp
ReferenceEquals(a, b);
“`
Kết quả:
“`text
false
“`
Đây là ví dụ rất tốt để ghi nhớ:
“`text
Value Equal
but
Not Same Reference
“`
—
# 41. Best Practice khi thiết kế Equality
Trước khi custom equality, nên kiểm tra:
“`text
[ ] Equality có business meaning rõ ràng không?
[ ] Nếu override Equals(), đã override GetHashCode() chưa?
[ ] Nếu overload ==, đã overload != chưa?
[ ] == và Equals() có cùng semantic không?
[ ] Property dùng để tính hash có immutable không?
[ ] Object này là Entity hay Value Object?
[ ] Record có phù hợp hơn class không?
[ ] Object có được sử dụng trong Dictionary hoặc HashSet không?
“`
—
# Kết luận
Không nên hiểu đơn giản:
“`text
== = reference
Equals = value
“`
Cách hiểu này không chính xác trong C#.
Chính xác hơn:
> `==` là equality operator và behavior phụ thuộc vào type hoặc operator overload.
Trong khi:
> `Equals()` là method dùng để biểu diễn logical equality và có thể được override.
Nếu muốn kiểm tra hai variable có trỏ tới đúng cùng một object:
“`csharp
ReferenceEquals()
“`
Nếu muốn custom equality:
“`text
Equals()
+
GetHashCode()
+
== / != nếu cần
“`
phải được thiết kế nhất quán.
Cuối cùng, chỉ cần nhớ ba dòng:
“`text
== → Equality Operator
Equals() → Logical / Value Equality
ReferenceEquals → Same Object Identity
“`
Hiểu được ba khái niệm này sẽ giúp tránh rất nhiều bug khi làm việc với:
“`text
Dictionary
HashSet
Entity
Value Object
Domain Model
LINQ
Collections
“`