Опубликован: 25.03.2010 | Доступ: свободный | Студентов: 1447 / 158 | Оценка: 4.31 / 4.00 | Длительность: 25:42:00
Лекция 3:

Обзор среды .NET Framework

Инициализация полей без конструктора

В C++ в классе можно простые переменные только объявлять, а инициализировать их нужно в конструкторе или устанавливать путем присваивания функциями Set() из клиента. В C# такое ограничение отсутствует и данные-члены класса можно инициализировать при их объявлении в классе. Примеры

  • Измените код класса MyClass нашего примера так, как показано ниже
using System;
    
namespace MyApp
{
    class MyClass
    {
        // Внутренние (по умолчанию) переменные - поля
        int x = 10, y = 15;
    
        // Конструктор по умолчанию
        public MyClass() { }
    
        // Конструктор с параметрами
        public MyClass(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
    
        // Общедоступные свойства класса
        public int X
        {
            get { return x; }
            set { x = value; }
        }
    
        public int Y
        {
            get { return y; }
            set { y = value; }
        }
    }
}
Листинг 3.10 . Инициализация полей без конструктора. Файл MyClass.cs
using System;
    
namespace MyApp
{
    class EntryPoint
    {
        // Точка входа в программу без аргументов
        static void Main()
        {
            // Настройка консоли
            Console.Title = "";
            Console.ForegroundColor = ConsoleColor.White;
            Console.CursorVisible = false;
    
            // Создаем экземпляр класса
            MyClass ob = new MyClass();
            Console.WriteLine("x = {0}; y = {1}", ob.X, ob.Y);
    
            // Создаем еще один экземпляр, 
            // используя ту же ссылочную переменную
            // Прежний объект будет утерян
            ob = new MyClass(100, 150);
            Console.WriteLine("x = {0}; y = {1}", ob.X, ob.Y);
    
            Console.ReadLine();
        }
    }
}
Листинг 3.11 . Инициализация полей без конструктора. Файл Program.cs

Обратите внимание, что при создании объекта класса MyClass используется одна и та же ссылочная переменная, после присвоения которой нового значения прежний брошенный объект становится добычей сборщика мусора.

После построения проекта получим результат


Отметим, что подобная прямая инициализация членов-данных в структурах недопустима. Перегрузка конструктора по умолчанию в структурах тоже недопустима. Чтобы убедиться в этом, объявите MyClass как структуру.

Виды данных в C#

В C# различают два вида типов: значимые ( value-based ) и ссылочные ( reference-based ). Переменная значимого типа представляет закрепленное за ней само значение оперативной памяти. Переменная ссылочного типа содержит лишь адрес области оперативной памяти с размещаемым в этой области значением. Все структурные переменные размещаются на стеке, а ссылочные - на управляемой куче ( managed heap ). К структурным типам относятся перечисления и структуры, а к ссылочным - классы, делегаты, интерфейсы.

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

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

  • Добавьте к нашему проекту файл с именем MyStruct.c s типа C #, выполнив команду Project/Add Clas s, и скопируйте в него код из файла MyClass.cs. Исправьте программу, чтобы она была такой
using System;
    
namespace MyApp
{
    struct MyStruct
    {
        // Внутренние (по умолчанию) переменные - поля
        int x, y;
    
        // Конструктор с параметрами
        public MyStruct(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
    
        // Конструктор по умолчанию перегружать нельзя
        // public MyStruct() { }
    
        // Общедоступные свойства класса
        public int X
        {
            get { return x; }
            set { x = value; }
        }
    
        public int Y
        {
            get { return y; }
            set { y = value; }
        }
    }
}
Листинг 3.12 . Код файла MyStruct.cs
using System;
    
namespace MyApp
{
    class MyClass
    {
        // Внутренние (по умолчанию) переменные - поля
        int x, y;
        
        // Конструктор с параметрами
        public MyClass(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
    
        // Конструктор по умолчанию
        public MyClass() { }
    
        // Общедоступные свойства класса
        public int X
        {
            get { return x; }
            set { x = value; }
        }
    
        public int Y
        {
            get { return y; }
            set { y = value; }
        }
    }
}
Листинг 3.13 . Код файла MyClass.cs
using System;
    
namespace MyApp
{
    class EntryPoint
    {
        // Точка входа в программу без аргументов
        static void Main()
        {
            // Настройка консоли
            Console.Title = "";
            Console.ForegroundColor = ConsoleColor.White;
            Console.CursorVisible = false;
    
            // Создаем две структуры
            MyStruct struct1 = new MyStruct(10, 15);
            MyStruct struct2 = new MyStruct();
    
            // Создаем два объекта
            MyClass object1 = new MyClass(10, 15);
            MyClass object2 = new MyClass();
    
            // Печатаем
            Console.WriteLine("Структуры до изменений:");
            Console.WriteLine("x = {0}; y = {1}", struct1.X, struct1.Y);
            Console.WriteLine("x = {0}; y = {1}", struct2.X, struct2.Y);
            Console.WriteLine("\nОбъекты до изменений:");
            Console.WriteLine("x = {0}; y = {1}", object1.X, object1.Y);
            Console.WriteLine("x = {0}; y = {1}", object2.X, object2.Y);
    
            // Присваиваем значения
            struct1 = struct2;// Копируются значения, struct1 существует
            object1 = object2; // Копируется адрес, object1 теряется  
    
            // Изменяем экземпляры
            struct2.X = 20; struct2.Y = 30;
            object2.X = 20; object2.Y = 30;
    
            // Печатаем
            Console.WriteLine("\nСтруктуры после изменений:");
            Console.WriteLine("x = {0}; y = {1}", struct1.X, struct1.Y);
            Console.WriteLine("x = {0}; y = {1}", struct2.X, struct2.Y);
            Console.WriteLine("\nОбъект после изменений:");
            Console.WriteLine("x = {0}; y = {1}", object1.X, object1.Y);
            Console.WriteLine("x = {0}; y = {1}", object2.X, object2.Y);    
    
            Console.ReadLine();
        }
    }
}
Листинг 3.14 . Код файла Program.cs

Результат выполнения будет таким


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

Общие сравнения значимых и ссылочных типов

Поставим некоторые предварительные вопросы и ответы на них в таблице

Таблица 3.4 . Сравнение структурных и ссылочных типов
Вопрос Значимые типы Ссылочные типы
Место размещения Стек Управляемая куча
Как будет представлена переменная, которой в качестве значения присвоен этот тип В виде локальной побитовой копии типа В виде указателя на область оперативной памяти, относящейся к объекту этого типа
Что может являться для них базовым типом Эти типы могут производиться только напрямую от типа System.ValueType Могут производиться от любого другого типа, кроме System.ValueType, если этот тип не является закрытым
Может ли этот тип выступать производителем другого типа Нет. Значимые типы всегда закрыты и дополнение их другими свойствами не предусмотрено. Поэтому они не могут выступать при наследовании в качестве базового для других типов Да, если этот ссылочный тип не определен как закрытый
Как производится передача параметров этого типа в функцию Только по значению, т.е. вызывающей функции передаются локальные копии значений переменных Передаются как ссылки, т.е. вызывающей функции передаются адреса размещения соответствующих объектов в оперативной памяти
Существует ли возможность переопределить встроенный метод Object.Finalize() Нет. Значимые типы никогда не размещаются в куче, поэтому к ним нельза применить функцию освобождения памяти Да, но не напрямую
Можно ли определить конструкторы для этого типа Да, но конструктор по умолчанию зарезервирован, поэтому все определяемые конструкторы должны иметь параметры Да, сколько угодно, лишь бы они различались сигнатурой
Когда происходит разрушение переменной данного типа Когда происходит выход из области видимости Во время процесса сборки мусора в управляемой куче с помощью механизма Garbage Collector (сборщик мусора)
Можно ли напрямую инициализировать данные при объявлении типа Нет Да
Можно ли перегружать конструктор по умолчанию Нет, он зарезервирован за системой Да, и обязательно, если определен хоть один конструктор с параметрами

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

Максим Филатов
Максим Филатов

Прошел курс. Получил код Dreamspark. Ввожу код на сайте, пишет:

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

 

Как активировать код?