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

Типы данных C#

Ссылочные и значимые типы

Экземпляры классов создаются методами налету в управляемой куче, а ссылки на них размещаются в стеке потока. Такие типы называются ссылочными, поскольку в программе представлены не самими данными, а ссылками на них. Их экземпляры еще называют неименованными данными. С другой стороны, простые (элементарные) типы, структуры и перечисления (перечни) выведены из класса System.ValueType и их экземпляры представлены переменными, которые являются самими данными. Такие типы называют значимыми, а представляющие их переменные называют именованными данными.

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

Объекты значимого типа поддерживают два вида синтаксиса:

  • Статическое объявление ( int x; Point point;)
  • Динамическое объявление ( int x = new int(); Point point = new Point(); )

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

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

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

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

using System;
    
namespace Test
{
    class Program
    {
        static void Main()
        {
            new Some();
    
            Console.ReadLine();
        }
    }
    
    struct Point
    {
        int x, y;
    
        public Point(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
    
        public void Show()
        {
            Console.WriteLine("x={0}; y={1}", x, y);
        }
    }
    
    class Some
    {
        //Point point;
    
        public Some()
        {
            Point point = new Point();
            point.Show();
        }
    }
}
Листинг 4.4. Сложные значимые типы

Упаковка и распаковка значимых типов

Иногда может потребоваться хранить значимый тип в управляемой куче с сохранением его адреса в ссылочной переменной. Ссылка должна иметь тип object, поскольку класс object является порождающим для всех типов C#. Такой процесс называется упаковкой значимого объекта. Синтаксис упаковки такой

object refValue = point;

Среда выполнения сама зарезервирует место в динамической области памяти и скопирует туда все данные значимого типа.

Упакованный объект можно передать как ссылочный параметр любому внешнему методу. Обратный процесс получения значимого объекта по его ссылке называется распаковкой. По ссылке упакованного объекта нельзя обратиться к его членам. Для этого объект нужно распаковать. Синтаксис распаковки такой

Point point = (Point)refValue;

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

Экземпляр класса (объект), размещенный при выполнении метода в управляемой куче и адресуемый в программе по ссылке, называется упакованным. Структура или перечисление, размещаемые при выполнении метода в стеке потока, называются распакованными. Здесь речь идет только о членах-данных этих программных единиц.

Поскольку структуры производятся от класса Systcode.ValueType, который, в свою очередь произошел от класса Systcode.Object, то значимый тип можно привести к ссылке на базовый класс Systcode.Object. Такая процедура называется упаковкой.

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

using System;
    
namespace Test
{
    struct Point
    {
        // Поля
        public int x, y;
    
        // Конструктор
        public Point(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
    
        // Функция доступа
        public void Set(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
    
        // Печать
        public void Show(string message)
        {
            Console.WriteLine(message + "x={0}, y={1}", x, y);
        }
    }
    
    // Класс с функцией Main не может иметь экземпляр
    // Поэтому все функции в этом классе должны быть статическими
    class EntryPoint
    {
    static Point valPoint3; // Поле в объекте-типе
    
    static void Main(string[] args)
    {
    // Настройка консоли
    Console.Title = "";
    Console.ForegroundColor = ConsoleColor.White;
    Console.CursorVisible = false;
    
    Point valPoint = new Point(5, 10);// Инициализировали и поместили в стек
    valPoint.Show("Исходный объект в стеке: ");
    object refPoint = valPoint;// Упаковка: ссылка в стеке, объект в куче
    ((Point)refPoint).Show("\nИсходный объект в куче: ");
    object refPoint1 = refPoint;// Еще одна ссылка на объект в куче
    CreatePackPoint(ref refPoint1);// Создали еще один объект в куче
    ((Point)refPoint1).Show("\nНовый объект в куче: ");
    
    Point valPoint1 = new Point();
    valPoint1.Show("\nЕще один локальный объект в стеке: ");
    
    Point valPoint2;// Статическое объявление локального объекта
    valPoint2.x = 50;// Обязательная инициализация
    valPoint2.y = 70;// Обязательная инициализация
    valPoint2.Show("\nИ еще один локальный объект в стеке: ");
    
    valPoint3.Show("\nОбъект-поле в типе: ");
    
    Console.ReadLine();// Для задержки окна
    }
    
        static void CreatePackPoint(ref object arg)
        {
            Point point = (Point)arg;// Распаковываем
            point.Set(20, 30);// Изменяем
            arg = point;//Опять упаковываем
        }
    }
}
Листинг 4.5. Упаковка и распаковка значимых типов
Максим Филатов
Максим Филатов

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

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

 

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