Objetos: creación de instancias de tipos

Una definición de clase o estructura es como un plano que especifica qué puede hacer el tipo. Un objeto es básicamente un bloque de memoria que se ha asignado y configurado de acuerdo con el plano. Un programa puede crear muchos objetos de la misma clase. Los objetos también se denominan instancias y pueden almacenarse en una variable con nombre, o en una matriz o colección. El código de cliente es el código que usa estas variables para llamar a los métodos y acceder a las propiedades públicas del objeto. En un lenguaje orientado a objetos, como C#, un programa típico consta de varios objetos que interactúan dinámicamente.

Nota

Los tipos estáticos se comportan de forma diferente a lo que se describe aquí. Para más información, vea Clases estáticas y sus miembros.

Instancias de estructura frente a Instancias de clase

Puesto que las clases son tipos de referencia, una variable de un objeto de clase contiene una referencia a la dirección del objeto del montón administrado. Si se asigna una segunda variable del mismo tipo a la primera variable, ambas variables hacen referencia al objeto de esa dirección. Este punto se analiza con más detalle más adelante en este artículo.

Las instancias de clases se crean mediante el operador new. En el ejemplo siguiente, Person es el tipo, y person1 y person2 son instancias u objetos de ese tipo.

using System;

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }
    // Other properties, methods, events...
}

class Program
{
    static void Main()
    {
        Person person1 = new Person("Leopold", 6);
        Console.WriteLine("person1 Name = {0} Age = {1}", person1.Name, person1.Age);

        // Declare new person, assign person1 to it.
        Person person2 = person1;

        // Change the name of person2, and person1 also changes.
        person2.Name = "Molly";
        person2.Age = 16;

        Console.WriteLine("person2 Name = {0} Age = {1}", person2.Name, person2.Age);
        Console.WriteLine("person1 Name = {0} Age = {1}", person1.Name, person1.Age);
    }
}
/*
    Output:
    person1 Name = Leopold Age = 6
    person2 Name = Molly Age = 16
    person1 Name = Molly Age = 16
*/

Dado que las estructuras son tipos de valor, una variable de un objeto de estructura contiene una copia de todo el objeto. También se pueden crear instancias de estructuras usando el operador new, pero esto no resulta necesario, como se muestra en el ejemplo siguiente:

using System;

namespace Example
{
    public struct Person
    {
        public string Name;
        public int Age;
        public Person(string name, int age)
        {
            Name = name;
            Age = age;
        }
    }

    public class Application
    {
        static void Main()
        {
            // Create  struct instance and initialize by using "new".
            // Memory is allocated on thread stack.
            Person p1 = new Person("Alex", 9);
            Console.WriteLine("p1 Name = {0} Age = {1}", p1.Name, p1.Age);

            // Create  new struct object. Note that  struct can be initialized
            // without using "new".
            Person p2 = p1;

            // Assign values to p2 members.
            p2.Name = "Spencer";
            p2.Age = 7;
            Console.WriteLine("p2 Name = {0} Age = {1}", p2.Name, p2.Age);

            // p1 values remain unchanged because p2 is  copy.
            Console.WriteLine("p1 Name = {0} Age = {1}", p1.Name, p1.Age);
        }
    }
    /*
        Output:
        p1 Name = Alex Age = 9
        p2 Name = Spencer Age = 7
        p1 Name = Alex Age = 9
    */
}

La memoria para p1 y p2 se asigna en la pila de subprocesos. Esta memoria se reclama junto con el tipo o método en el que se declara. Este es uno de los motivos por los que se copian las estructuras en la asignación. Por el contrario, la memoria que se asigna a una instancia de clase la reclama automáticamente (recolección de elementos no utilizados) Common Language Runtime cuando todas las referencias al objeto se han salido del ámbito. No es posible destruir de forma determinante un objeto de clase como en C++. Para obtener más información sobre la recolección de elementos no utilizados en .NET, vea Recolección de elementos no utilizados.

Nota

La asignación y desasignación de memoria en el montón administrado están muy optimizadas en Common Language Runtime. En la mayoría de los casos, no existe ninguna diferencia significativa en el costo de rendimiento entre asignar una instancia de clase en el montón y asignar una instancia de estructura en la pila.

Identidad de objeto frente a igualdad de valores

Cuando se comparan dos objetos para comprobar si son iguales, primero debe determinar si quiere saber si las dos variables representan el mismo objeto en la memoria o si los valores de uno o varios de sus campos son equivalentes. Si tiene previsto comparar valores, debe tener en cuenta si los objetos son instancias de tipos de valor (estructuras) o tipos de referencia (clases, delegados y matrices).

  • Para determinar si dos instancias de clase hacen referencia a la misma ubicación en la memoria (lo que significa que tienen la misma identidad), use el método estático Object.Equals. (System.Object es la clase base implícita para todos los tipos de valor y tipos de referencia, incluidas las clases y estructuras definidas por el usuario).

  • Para determinar si los campos de instancia de dos instancias de estructura presentan los mismos valores, use el método ValueType.Equals. Dado que todas las estructuras heredan implícitamente de System.ValueType, se llama al método directamente en el objeto, como se muestra en el ejemplo siguiente:

    // Person is defined in the previous example.
    
    //public struct Person
    //{
    //    public string Name;
    //    public int Age;
    //    public Person(string name, int age)
    //    {
    //        Name = name;
    //        Age = age;
    //    }
    //}
    
    Person p1 = new Person("Wallace", 75);
    Person p2 = new Person("", 42);
    p2.Name = "Wallace";
    p2.Age = 75;
    
    if (p2.Equals(p1))
        Console.WriteLine("p2 and p1 have the same values.");
    
    // Output: p2 and p1 have the same values.
    

    En algunos casos, la implementación de System.ValueType de Equals utiliza la conversión boxing y la reflexión. Para obtener información sobre cómo proporcionar un algoritmo de igualdad eficaz que sea específico del tipo, consulte Definición de la igualdad de valores para un tipo. Los registros son tipos de referencia que usan semántica de valores para la igualdad.

  • Para determinar si los valores de los campos de dos instancias de clase son iguales, puede usar el método Equals o el operador ==. En cambio, úselos solo si la clase los ha invalidado o sobrecargado para proporcionar una definición personalizada de lo que significa "igualdad" para los objetos de ese tipo. La clase también puede implementar la interfaz IEquatable<T> o la interfaz IEqualityComparer<T>. Ambas interfaces proporcionan métodos que pueden servir para comprobar la igualdad de valores. Al diseñar sus propias clases que invaliden Equals, asegúrese de seguir las instrucciones descritas en Procedimiento: Definición de la igualdad de valores para un tipo y Object.Equals(Object).

Para obtener más información: