Baca dalam bahasa Inggris

Bagikan melalui


Cara menguji kesetaraan referensi (Identitas) (Panduan Pemrograman C#)

Anda tidak perlu menerapkan logika kustom apa pun untuk mendukung perbandingan kesetaraan referensi dalam jenis Anda. Fungsionalitas ini disediakan untuk semua jenis dengan metode Object.ReferenceEquals statis.

Contoh berikut menunjukkan cara menentukan apakah dua variabel memiliki kesetaraan referensi, yang berarti bahwa mereka merujuk ke objek yang sama dalam memori.

Contohnya juga menunjukkan mengapa Object.ReferenceEquals selalu mengembalikan false untuk jenis nilai. Ini karena boxing, yang menyebabkan terciptanya instans objek yang terpisah untuk setiap argumen tipe nilai. Selain itu, Anda tidak boleh menggunakan ReferenceEquals untuk menentukan kesetaraan string.

Contoh

using System.Text;

namespace TestReferenceEquality
{
    struct TestStruct
    {
        public int Num { get; private set; }
        public string Name { get; private set; }

        public TestStruct(int i, string s) : this()
        {
            Num = i;
            Name = s;
        }
    }

    class TestClass
    {
        public int Num { get; set; }
        public string? Name { get; set; }
    }

    class Program
    {
        static void Main()
        {
            // Demonstrate reference equality with reference types.
            #region ReferenceTypes

            // Create two reference type instances that have identical values.
            TestClass tcA = new TestClass() { Num = 1, Name = "New TestClass" };
            TestClass tcB = new TestClass() { Num = 1, Name = "New TestClass" };

            Console.WriteLine($"ReferenceEquals(tcA, tcB) = {Object.ReferenceEquals(tcA, tcB)}"); // false

            // After assignment, tcB and tcA refer to the same object.
            // They now have reference equality.
            tcB = tcA;
            Console.WriteLine($"After assignment: ReferenceEquals(tcA, tcB) = {Object.ReferenceEquals(tcA, tcB)}"); // true

            // Changes made to tcA are reflected in tcB. Therefore, objects
            // that have reference equality also have value equality.
            tcA.Num = 42;
            tcA.Name = "TestClass 42";
            Console.WriteLine($"tcB.Name = {tcB.Name} tcB.Num: {tcB.Num}");
            #endregion

            // Demonstrate that two value type instances never have reference equality.
            #region ValueTypes

            TestStruct tsC = new TestStruct( 1, "TestStruct 1");

            // Value types are boxed into separate objects when passed to ReferenceEquals.
            // Even if the same variable is used twice, boxing ensures they are different instances.
            TestStruct tsD = tsC;
            Console.WriteLine($"After assignment: ReferenceEquals(tsC, tsD) = {Object.ReferenceEquals(tsC, tsD)}"); // false
            #endregion

            #region stringRefEquality
            // Constant strings within the same assembly are always interned by the runtime.
            // This means they are stored in the same location in memory. Therefore,
            // the two strings have reference equality although no assignment takes place.
            string strA = "Hello world!";
            string strB = "Hello world!";
            Console.WriteLine($"ReferenceEquals(strA, strB) = {Object.ReferenceEquals(strA, strB)}"); // true

            // After a new string is assigned to strA, strA and strB
            // are no longer interned and no longer have reference equality.
            strA = "Goodbye world!";
            Console.WriteLine($"strA = '{strA}' strB = '{strB}'");

            Console.WriteLine("After strA changes, ReferenceEquals(strA, strB) = {0}",
                            Object.ReferenceEquals(strA, strB)); // false

            // A string that is created at runtime cannot be interned.
            StringBuilder sb = new StringBuilder("Hello world!");
            string stringC = sb.ToString();
            // False:
            Console.WriteLine($"ReferenceEquals(stringC, strB) = {Object.ReferenceEquals(stringC, strB)}");

            // The string class overloads the == operator to perform an equality comparison.
            Console.WriteLine($"stringC == strB = {stringC == strB}"); // true

            #endregion

            // Keep the console open in debug mode.
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }
    }
}

/* Output:
    ReferenceEquals(tcA, tcB) = False
    After assignment: ReferenceEquals(tcA, tcB) = True
    tcB.Name = TestClass 42 tcB.Num: 42
    After assignment: ReferenceEquals(tsC, tsD) = False
    ReferenceEquals(strA, strB) = True
    strA = "Goodbye world!" strB = "Hello world!"
    After strA changes, ReferenceEquals(strA, strB) = False
    ReferenceEquals(stringC, strB) = False
    stringC == strB = True
*/

Implementasi Equals di kelas dasar universal System.Object juga melakukan pengecekan kesetaraan referensi, tetapi sebaiknya tidak menggunakan ini, karena jika suatu kelas kebetulan melakukan override pada metode, hasilnya mungkin tidak seperti yang Anda harapkan. Hal yang sama berlaku untuk operator == dan !=. Ketika mereka beroperasi pada jenis referensi, perilaku default == dan != adalah melakukan pemeriksaan kesetaraan referensi. Namun, kelas turunan dapat membebani operator untuk melakukan pemeriksaan kesetaraan nilai. Untuk meminimalkan potensi kesalahan, yang terbaik adalah selalu menggunakan ReferenceEquals ketika Anda harus menentukan apakah dua objek memiliki kesetaraan referensi.

String konstanta dalam rakitan yang sama selalu diinternasi oleh runtime. Artinya, hanya satu instance dari setiap string literal unik yang disimpan. Namun, runtime tidak menjamin bahwa string yang dibuat pada run time diinternasi, juga tidak menjamin bahwa dua string konstanta yang sama dalam rakitan berbeda diinternasi.

Catatan

ReferenceEquals mengembalikan false untuk tipe nilai karena pembungkusan, karena setiap argumen dibungkus secara independen ke dalam objek terpisah.

Lihat juga


Sumber Daya Tambahan:

Dokumentasi