Поделиться через


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

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

Примечание.

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

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

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

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

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 = {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 Person("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 см. в статье "Сборка мусора".

Примечание.

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

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

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

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

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

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

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

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

Дополнительные сведения: