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

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

Примечание.

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

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

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

Экземпляры классов создаются с помощью new оператора. В приведенном ниже примере Person является типом, а person1 и person2 — экземплярами или объектами этого типа.

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

Так как структуры являются типами значений, в переменной объекта структуры хранится копия всего объекта. Экземпляры структур также можно создать с помощью 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 = {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
    */
}

Память для 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.
    

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

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

Дополнительные сведения см. по ссылке .