Objetos: creación de instancias de tipos

Una definición de clase o estructura es como un plano técnico que especifica lo que puede hacer el tipo. Un objeto es un bloque de memoria que el programa asigna y configura según el plano técnico. Un programa puede crear muchos objetos de la misma clase. También se puede llamar a las instancias de objetos. Puede almacenarlos en una variable con nombre o en una matriz o colección. El código de cliente 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 en este artículo. Para obtener más información, vea Clases estáticas y miembros de clase estática.

Instancias de estructura frente a instancias de clase

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

Puede crear instancias de clases mediante el new operador . En el ejemplo siguiente, Person es el tipo y person1 y person2 son instancias, o objetos, de ese tipo.

using System;

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

class Program
{
    static void Main()
    {
        Person person1 = new("Leopold", 6);
        Console.WriteLine($"person1 Name = {person1.Name} Age = {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 = {person2.Name} Age = {person2.Age}");
        Console.WriteLine($"person1 Name = {person1.Name} Age = {person1.Age}");

        /*
            Output:
            person1 Name = Leopold Age = 6
            person2 Name = Molly Age = 16
            person1 Name = Molly Age = 16
        */
    }
}

Dado que los structs son tipos de valor, una variable de un objeto struct contiene una copia de todo el objeto. También puede crear instancias de structs mediante el new operador , pero no es necesario usarla, 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("Alex", 9);
            Console.WriteLine($"p1 Name = {p1.Name} Age = {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 = {p2.Name} Age = {p2.Age}");

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

La pila del hilo asigna memoria tanto para p1 como para p2. El programa recupera esa memoria junto con el tipo o método en el que la declaras. Esta administración de la memoria es una de las razones por las que las estructuras se copian al asignarlas. Por el contrario, el tiempo de ejecución de lenguaje común recupera automáticamente (mediante la recolección de basura) la memoria que asigna a una instancia de clase cuando todas las referencias al objeto quedan fuera del ámbito. No puede destruir de forma determinista un objeto de clase como puede 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:

El tiempo de ejecución de lenguaje común optimiza en gran medida la asignación y desasignación de memoria en el montón administrado. En la mayoría de los casos, no hay una diferencia significativa en el coste 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

Al comparar dos objetos por igualdad, primero decida si desea saber si las dos variables representan el mismo objeto en memoria o si los valores de uno o varios de sus campos son equivalentes. Si desea comparar valores, considere si los objetos son instancias de tipos de valor (structs) o tipos de referencia (clases, delegados, matrices).

  • Use el método estático Object.ReferenceEquals 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). (System.Object es la clase base implícita para todos los tipos de valor y tipos de referencia, incluidas las estructuras y clases definidas por el usuario).

  • De forma predeterminada, el ValueType.Equals método determina si los campos de instancia de dos instancias de estructura tienen los mismos valores. Dado que todas las estructuras heredan implícitamente de System.ValueType, llame al método directamente en el objeto como se muestra en el ejemplo siguiente:

    // Person is defined in the previous example.
    
    //public struct Person(string name, int age)
    //{
    //    public string Name { get; set; } = name;
    //    public int Age { get; set; } = age;
    //}
    
    Person p1 = new("Wallace", 75);
    Person p2 = new("", 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.
    

    La implementación predeterminada System.ValueType de Equals utiliza boxing y reflexión en algunos casos. Para obtener información sobre cómo proporcionar un algoritmo de igualdad eficiente específico para su tipo, consulte Implementar la igualdad por su cuenta cuando un tipo no puede ser un registro. Los registros son tipos de referencia que usan la semántica de valores para la igualdad.

  • Para determinar si los valores de los campos de dos instancias de clase son iguales, es posible que pueda usar el Equals método 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 IEquatable<T> interfaz o la IEqualityComparer<T> interfaz . Ambas interfaces proporcionan métodos que se pueden usar para probar la igualdad de valores. Al diseñar sus propias clases que sobrescriben Equals, asegúrese de seguir las directrices indicadas en Implementar la igualdad por su cuenta cuando un tipo no puede ser un registro y Object.Equals(Object).

Para obtener más información, consulte: