Acara
Membangun Aplikasi dan Agen AI
17 Mar, 21 - 21 Mar, 10
Bergabunglah dengan seri meetup untuk membangun solusi AI yang dapat diskalakan berdasarkan kasus penggunaan dunia nyata dengan sesama pengembang dan pakar.
Daftar sekarangBrowser ini sudah tidak didukung.
Mutakhirkan ke Microsoft Edge untuk memanfaatkan fitur, pembaruan keamanan, dan dukungan teknis terkini.
Rekaman secara otomatis menerapkan kesetaraan nilai. Pertimbangkan untuk menentukan record
alih-alih class
ketika jenis Anda memodelkan data dan harus menerapkan kesetaraan nilai.
Saat Anda menentukan kelas atau struktur, Anda memutuskan apakah masuk akal untuk membuat definisi kustom kesetaraan nilai (atau yang setara) untuk jenis tersebut. Biasanya, Anda menerapkan kesetaraan nilai ketika Anda ingin menambahkan objek jenis ke koleksi, atau ketika tujuan utamanya adalah untuk menyimpan sekumpulan bidang atau properti. Anda dapat mendasarkan definisi kesetaraan nilai Anda pada perbandingan semua bidang dan properti dalam jenis, atau Anda dapat mendasarkan definisi pada subset.
Dalam kedua kasus, dan dalam kedua kelas dan struktur, implementasi Anda harus mengikuti lima jaminan kesetaraan (untuk aturan berikut, asumsikan bahwa x
, y
dan z
tidak null):
Properti refleksif: x.Equals(x)
mengembalikan true
.
Properti simetris: x.Equals(y)
mengembalikan nilai yang sama dengan y.Equals(x)
.
Properti transitif: jika (x.Equals(y) && y.Equals(z))
mengembalikan true
, maka x.Equals(z)
mengembalikan true
.
Pemanggilan x.Equals(y)
berturut-turut mengembalikan nilai yang sama selama objek yang dirujuk oleh x dan y tidak dimodifikasi.
Nilai non-null tidak sama dengan null. Namun, x.Equals(y)
menjadi pengecualian ketika x
null. Itu melanggar aturan 1 atau 2, tergantung pada argumen pada Equals
.
Setiap struktur yang Anda tentukan sudah memiliki implementasi default kesetaraan nilai yang diwarisinya dari System.ValueType ambil alih terhadap metode Object.Equals(Object). Implementasi ini menggunakan refleksi untuk memeriksa semua bidang dan properti dalam jenis. Meskipun implementasi ini menciptakan hasil yang benar, itu justru relatif lambat dibandingkan dengan implementasi kustom yang Anda tulis khusus untuk jenis tersebut.
Detail implementasi untuk kesetaraan nilai berbeda untuk kelas dan struktur. Namun, kelas dan struktur memerlukan langkah-langkah dasar yang sama untuk menerapkan kesetaraan:
Ambil alih metode virtualObject.Equals(Object). Dalam kebanyakan kasus, implementasi Anda terhadap bool Equals( object obj )
hanya boleh memanggil metode khusus jenis Equals
yang merupakan implementasi antarmuka System.IEquatable<T>. (Lihat langkah 2.)
Terapkan antarmuka System.IEquatable<T> dengan menyediakan metode khusus jenis Equals
. Di sinilah perbandingan kesetaraan aktual dilakukan. Misalnya, Anda mungkin memutuskan untuk menentukan kesetaraan dengan hanya membandingkan satu atau dua bidang dalam jenis Anda. Jangan membuat pengecualian dari Equals
. Untuk kelas yang terkait dengan pewarisan:
Metode ini hanya boleh memeriksa bidang yang dideklarasikan dalam kelas. Ini harus memanggil base.Equals
untuk memeriksa bidang yang ada di kelas dasar. (Jangan panggil base.Equals
jika jenis mewarisi langsung dari Object, karena implementasi Object dari Object.Equals(Object) melakukan pemeriksaan kesetaraan referensi.)
Dua variabel harus dianggap sama hanya jika jenis run-time variabel yang dibandingkan sama. Selain itu, pastikan bahwa IEquatable
implementasi Equals
metode untuk jenis run-time digunakan jika jenis run-time dan compile-time variabel berbeda. Salah satu strategi untuk memastikan jenis run-time selalu dibandingkan dengan benar adalah hanya menerapkan IEquatable
di sealed
kelas. Untuk informasi selengkapnya, lihat contoh kelas selanjutnya di artikel ini.
Opsional tetapi disarankan: Kelebihan beban operator == dan !=.
Ambil alih Object.GetHashCode sehingga dua objek yang memiliki kesetaraan nilai menghasilkan hash yang sama.
Opsional: Untuk mendukung definisi untuk "lebih dari" atau "kurang dari," terapkan antarmuka IComparable<T> untuk jenis Anda, dan juga kelebihan beban operator <= dan >=.
Catatan
Anda dapat menggunakan rekaman untuk mendapatkan semantik kesetaraan nilai tanpa kode boilerplate yang tidak perlu.
Contoh berikut menunjukkan cara menerapkan kesetaraan nilai dalam kelas (jenis referensi).
namespace ValueEqualityClass;
class TwoDPoint : IEquatable<TwoDPoint>
{
public int X { get; private set; }
public int Y { get; private set; }
public TwoDPoint(int x, int y)
{
if (x is (< 1 or > 2000) || y is (< 1 or > 2000))
{
throw new ArgumentException("Point must be in range 1 - 2000");
}
this.X = x;
this.Y = y;
}
public override bool Equals(object obj) => this.Equals(obj as TwoDPoint);
public bool Equals(TwoDPoint p)
{
if (p is null)
{
return false;
}
// Optimization for a common success case.
if (Object.ReferenceEquals(this, p))
{
return true;
}
// If run-time types are not exactly the same, return false.
if (this.GetType() != p.GetType())
{
return false;
}
// Return true if the fields match.
// Note that the base class is not invoked because it is
// System.Object, which defines Equals as reference equality.
return (X == p.X) && (Y == p.Y);
}
public override int GetHashCode() => (X, Y).GetHashCode();
public static bool operator ==(TwoDPoint lhs, TwoDPoint rhs)
{
if (lhs is null)
{
if (rhs is null)
{
return true;
}
// Only the left side is null.
return false;
}
// Equals handles case of null on right side.
return lhs.Equals(rhs);
}
public static bool operator !=(TwoDPoint lhs, TwoDPoint rhs) => !(lhs == rhs);
}
// For the sake of simplicity, assume a ThreeDPoint IS a TwoDPoint.
class ThreeDPoint : TwoDPoint, IEquatable<ThreeDPoint>
{
public int Z { get; private set; }
public ThreeDPoint(int x, int y, int z)
: base(x, y)
{
if ((z < 1) || (z > 2000))
{
throw new ArgumentException("Point must be in range 1 - 2000");
}
this.Z = z;
}
public override bool Equals(object obj) => this.Equals(obj as ThreeDPoint);
public bool Equals(ThreeDPoint p)
{
if (p is null)
{
return false;
}
// Optimization for a common success case.
if (Object.ReferenceEquals(this, p))
{
return true;
}
// Check properties that this class declares.
if (Z == p.Z)
{
// Let base class check its own fields
// and do the run-time type comparison.
return base.Equals((TwoDPoint)p);
}
else
{
return false;
}
}
public override int GetHashCode() => (X, Y, Z).GetHashCode();
public static bool operator ==(ThreeDPoint lhs, ThreeDPoint rhs)
{
if (lhs is null)
{
if (rhs is null)
{
// null == null = true.
return true;
}
// Only the left side is null.
return false;
}
// Equals handles the case of null on right side.
return lhs.Equals(rhs);
}
public static bool operator !=(ThreeDPoint lhs, ThreeDPoint rhs) => !(lhs == rhs);
}
class Program
{
static void Main(string[] args)
{
ThreeDPoint pointA = new ThreeDPoint(3, 4, 5);
ThreeDPoint pointB = new ThreeDPoint(3, 4, 5);
ThreeDPoint pointC = null;
int i = 5;
Console.WriteLine("pointA.Equals(pointB) = {0}", pointA.Equals(pointB));
Console.WriteLine("pointA == pointB = {0}", pointA == pointB);
Console.WriteLine("null comparison = {0}", pointA.Equals(pointC));
Console.WriteLine("Compare to some other type = {0}", pointA.Equals(i));
TwoDPoint pointD = null;
TwoDPoint pointE = null;
Console.WriteLine("Two null TwoDPoints are equal: {0}", pointD == pointE);
pointE = new TwoDPoint(3, 4);
Console.WriteLine("(pointE == pointA) = {0}", pointE == pointA);
Console.WriteLine("(pointA == pointE) = {0}", pointA == pointE);
Console.WriteLine("(pointA != pointE) = {0}", pointA != pointE);
System.Collections.ArrayList list = new System.Collections.ArrayList();
list.Add(new ThreeDPoint(3, 4, 5));
Console.WriteLine("pointE.Equals(list[0]): {0}", pointE.Equals(list[0]));
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/* Output:
pointA.Equals(pointB) = True
pointA == pointB = True
null comparison = False
Compare to some other type = False
Two null TwoDPoints are equal: True
(pointE == pointA) = False
(pointA == pointE) = False
(pointA != pointE) = True
pointE.Equals(list[0]): False
*/
Pada kelas (jenis referensi), implementasi default pada kedua metode Object.Equals(Object) melakukan perbandingan kesetaraan referensi, bukan pemeriksaan kesetaraan nilai. Ketika pelaksana mengambil alih metode virtual, tujuannya adalah untuk memberinya nilai semantik kesetaraan.
Operator ==
dan !=
dapat digunakan dengan kelas bahkan jika kelas tidak membebaninya. Namun, perilaku default berfungsi untuk melakukan pemeriksaan kesetaraan referensi. Dalam kelas, jika Anda membebani metode Equals
Secara berlebih, Anda harus membebani operator ==
dan !=
, tetapi tidak diperlukan.
Penting
Contoh kode sebelumnya mungkin tidak menangani setiap skenario pewarisan seperti yang Anda harapkan. Pertimbangkan gambar berikut:
TwoDPoint p1 = new ThreeDPoint(1, 2, 3);
TwoDPoint p2 = new ThreeDPoint(1, 2, 4);
Console.WriteLine(p1.Equals(p2)); // output: True
Kode ini melaporkan bahwa p1
sama dengan p2
meskipun ada perbedaan pada nilai z
. Perbedaan diabaikan karena pengompilasi memilih implementasi TwoDPoint
berdasarkan jenis waktu kompilasi IEquatable
.
Kesetaraan nilai bawaan jenis record
menangani skenario seperti ini dengan benar. Jika TwoDPoint
dan ThreeDPoint
adalah jenis record
, hasil dari p1.Equals(p2)
adalah False
. Untuk informasi selengkapnya, lihat Kesetaraan dalam record
hierarki pewarisan jenis.
Contoh berikut menunjukkan cara menerapkan kesetaraan nilai dalam kelas (jenis nilai):
namespace ValueEqualityStruct
{
struct TwoDPoint : IEquatable<TwoDPoint>
{
public int X { get; private set; }
public int Y { get; private set; }
public TwoDPoint(int x, int y)
: this()
{
if (x is (< 1 or > 2000) || y is (< 1 or > 2000))
{
throw new ArgumentException("Point must be in range 1 - 2000");
}
X = x;
Y = y;
}
public override bool Equals(object? obj) => obj is TwoDPoint other && this.Equals(other);
public bool Equals(TwoDPoint p) => X == p.X && Y == p.Y;
public override int GetHashCode() => (X, Y).GetHashCode();
public static bool operator ==(TwoDPoint lhs, TwoDPoint rhs) => lhs.Equals(rhs);
public static bool operator !=(TwoDPoint lhs, TwoDPoint rhs) => !(lhs == rhs);
}
class Program
{
static void Main(string[] args)
{
TwoDPoint pointA = new TwoDPoint(3, 4);
TwoDPoint pointB = new TwoDPoint(3, 4);
int i = 5;
// True:
Console.WriteLine("pointA.Equals(pointB) = {0}", pointA.Equals(pointB));
// True:
Console.WriteLine("pointA == pointB = {0}", pointA == pointB);
// True:
Console.WriteLine("object.Equals(pointA, pointB) = {0}", object.Equals(pointA, pointB));
// False:
Console.WriteLine("pointA.Equals(null) = {0}", pointA.Equals(null));
// False:
Console.WriteLine("(pointA == null) = {0}", pointA == null);
// True:
Console.WriteLine("(pointA != null) = {0}", pointA != null);
// False:
Console.WriteLine("pointA.Equals(i) = {0}", pointA.Equals(i));
// CS0019:
// Console.WriteLine("pointA == i = {0}", pointA == i);
// Compare unboxed to boxed.
System.Collections.ArrayList list = new System.Collections.ArrayList();
list.Add(new TwoDPoint(3, 4));
// True:
Console.WriteLine("pointA.Equals(list[0]): {0}", pointA.Equals(list[0]));
// Compare nullable to nullable and to non-nullable.
TwoDPoint? pointC = null;
TwoDPoint? pointD = null;
// False:
Console.WriteLine("pointA == (pointC = null) = {0}", pointA == pointC);
// True:
Console.WriteLine("pointC == pointD = {0}", pointC == pointD);
TwoDPoint temp = new TwoDPoint(3, 4);
pointC = temp;
// True:
Console.WriteLine("pointA == (pointC = 3,4) = {0}", pointA == pointC);
pointD = temp;
// True:
Console.WriteLine("pointD == (pointC = 3,4) = {0}", pointD == pointC);
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/* Output:
pointA.Equals(pointB) = True
pointA == pointB = True
Object.Equals(pointA, pointB) = True
pointA.Equals(null) = False
(pointA == null) = False
(pointA != null) = True
pointA.Equals(i) = False
pointE.Equals(list[0]): True
pointA == (pointC = null) = False
pointC == pointD = True
pointA == (pointC = 3,4) = True
pointD == (pointC = 3,4) = True
*/
}
Untuk struktur, implementasi default Object.Equals(Object) (yang merupakan versi yang ditimpa pada System.ValueType) melakukan pemeriksaan kesetaraan nilai dengan menggunakan pantulan untuk membandingkan nilai setiap bidang dalam jenis. Ketika pelaksana mengambil alih metode virtual Equals
dalam struktur, tujuannya adalah untuk memberikan cara yang lebih efisien untuk melakukan pemeriksaan kesetaraan nilai dan secara opsional untuk mendasarkan perbandingan pada beberapa subset bidang atau properti struct.
Operator == dan != tidak dapat beroperasi pada struktur kecuali struktur secara eksplisit membebani mereka.
Umpan balik .NET
.NET adalah proyek sumber terbuka. Pilih tautan untuk memberikan umpan balik:
Acara
Membangun Aplikasi dan Agen AI
17 Mar, 21 - 21 Mar, 10
Bergabunglah dengan seri meetup untuk membangun solusi AI yang dapat diskalakan berdasarkan kasus penggunaan dunia nyata dengan sesama pengembang dan pakar.
Daftar sekarangPelatihan
Modul
Pilih tipe data yang benar dalam kode C# Anda - Training
Pilih jenis data yang benar untuk kode Anda dari beberapa jenis dasar yang digunakan dalam C#.
Dokumentasi
Cara menguji kesetaraan referensi (Identitas) - C#
Pelajari cara menguji kesetaraan referensi (Identitas). Lihat contoh kode dan lihat sumber daya tambahan yang tersedia.
Pelajari tentang perbandingan kesamaan. Lihat deskripsi 'kesamaan nilai' serta 'kesamaan referensi', dan lihat sumber daya tambahan.
Hasilkan C# Equals dan GetHashCode Method Overrides - Visual Studio (Windows)
Pelajari cara menggunakan menu Tindakan Cepat dan Pemfaktoran Ulang untuk menghasilkan metode Equals dan GetHashCode.
Pelajari cara membebani operator C# dan operator C# mana yang kelebihan beban. Secara umum, operator unary, aritmetic, equality, dan comparison kelebihan beban.