Compartir a través de


Cómo probar la igualdad de referencia (Identidad) (Guía de programación de C#)

No es necesario implementar ninguna lógica personalizada para admitir comparaciones de igualdad de referencia en los tipos. Esta funcionalidad es proporcionada para todos los tipos por el método estático Object.ReferenceEquals.

En el ejemplo siguiente se muestra cómo determinar si dos variables tienen igualdad de referencia, lo que significa que hacen referencia al mismo objeto en memoria.

En el ejemplo también se muestra por qué Object.ReferenceEquals siempre devuelve false para los tipos de valor. Esto se debe a la conversión boxing, que crea instancias de objeto independientes para cada argumento de tipo de valor. Además, no se debe usar ReferenceEquals para determinar la igualdad de cadenas.

Ejemplo

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
*/

La implementación de Equals en la clase base universal System.Object también realiza una comprobación de igualdad de referencia, pero es mejor no usar esto porque, si una clase pasa a invalidar el método, es posible que los resultados no sean lo que espera. Lo mismo sucede con los operadores == y !=. Cuando funcionan en tipos de referencia, el comportamiento predeterminado de == y != es realizar una comprobación de igualdad de referencia. Sin embargo, las clases derivadas pueden sobrecargar el operador para realizar una comprobación de igualdad de valores. Para minimizar la posibilidad de error, es mejor usar siempre ReferenceEquals cuando tenga que determinar si dos objetos tienen igualdad de referencia.

El runtime siempre aplica el método Intern a las cadenas constantes dentro del mismo ensamblado. Es decir, solo se mantiene una instancia de cada cadena literal única. Sin embargo, el tiempo de ejecución no garantiza que las cadenas creadas en tiempo de ejecución estén internas, ni tampoco garantiza que dos cadenas constantes iguales en ensamblajes diferentes se internen.

Nota

ReferenceEquals devuelve false para los tipos de valor debido a la conversión boxing, ya que cada argumento queda con conversión boxing en un objeto separado.

Consulte también