Объекты — создание экземпляров типов

Определение класса или структуры похоже на схему, указывающую, что может сделать тип. Объект — это блок памяти, который программа выделяет и настраивает в соответствии с схемой. Программа может создавать множество объектов одного класса. Объекты также можно называть экземплярами. Их можно хранить в именованной переменной или в массиве или коллекции. Клиентский код использует эти переменные для вызова методов и доступа к общедоступным свойствам объекта. В объектно-ориентированном языке, например C#, типичная программа состоит из нескольких объектов, взаимодействующих динамически.

Примечание.

Статические типы ведут себя не так, как описано в этой статье. Дополнительные сведения см. в статье Статические классы и члены статических классов.

Экземпляры структур и экземпляры классов

Поскольку классы являются ссылочными типами, переменная объекта класса содержит ссылку на адрес объекта в управляемой куче. При назначении второй переменной одного типа первой переменной обе переменные ссылаются на объект по указанному адресу. В этой статье этот вопрос далее рассматривается более подробно.

Экземпляры классов создаются с помощью new оператора. В следующем примере Person тип и person1person2 являются экземплярами или объектами этого типа.

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

Поскольку структуры являются типами значений, переменная объекта структуры содержит копию всего объекта. Вы также можете создавать экземпляры структур с помощью new оператора, но его не нужно использовать, как показано в следующем примере:

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

Стек потока выделяет память как для p1, так и для p2. Программа освобождает эту память вместе с типом или методом, в котором она объявлена. Такое управление памятью — одна из причин, по которой структуры копируются при присваивании. В отличие от этого, общеязыковая среда выполнения (CLR) автоматически освобождает память, которую она выделила для экземпляра класса, когда все ссылки на объект выходят из области видимости. Невозможно детерминированно уничтожить объект класса, как можно в C++. Дополнительные сведения о сборке мусора в .NET см. в статье "Сборка мусора".

Примечание.

Общеязыковая среда выполнения значительно оптимизирует выделение и освобождение памяти в управляемой куче. В большинстве случаев нет существенной разницы в затратах на производительность между выделением экземпляра класса в куче и экземпляра структуры в стеке.

Удостоверение объекта и равенство значений

При сравнении двух объектов для равенства сначала определите, представляют ли эти две переменные один и тот же объект в памяти или если значения одного или нескольких их полей эквивалентны. Если вы хотите сравнить значения, рассмотрите, являются ли объекты экземплярами типов значений (структур) или ссылочных типов (классы, делегаты, массивы).

  • Используйте статический Object.ReferenceEquals метод, чтобы определить, относятся ли два экземпляра класса к одному расположению в памяти (что означает, что они имеют одно и то же удостоверение). (System.Object является неявным базовым классом для всех типов значений и ссылочных типов, включая определяемые пользователем структуры и классы.)

  • По умолчанию метод ValueType.Equals определяет, имеют ли поля экземпляров в двух экземплярах структур одинаковые значения. Так как все структуры неявно наследуются System.ValueType, метод вызывается непосредственно в объекте, как показано в следующем примере:

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

    Реализация по умолчанию System.ValueTypeEquals использует упаковку и отражение в некоторых случаях. Сведения о том, как реализовать эффективный алгоритм проверки на равенство для конкретного типа, см. в разделе Самостоятельная реализация равенства, если тип не может быть записью. Записи — это ссылочные типы, которые используют семантику значений для определения равенства.

  • Чтобы определить, равны ли значения полей в двух экземплярах класса, можно использовать Equals метод или оператор == . Однако используйте их только в том случае, если класс переопределяет или перегружает их, чтобы предоставить пользовательское определение того, что означает "равенство" для объектов этого типа. Класс также может реализовать IEquatable<T> интерфейс или IEqualityComparer<T> интерфейс. Оба интерфейса предоставляют методы, которые можно использовать для проверки равенства значений. При проектировании собственных классов, которые переопределяют Equals, обязательно следуйте рекомендациям, приведённым в руководстве Реализуйте равенство самостоятельно, если тип не может быть записью и Object.Equals(Object).

Дополнительные сведения можно найти здесь