Студопедия

Главная страница Случайная страница

КАТЕГОРИИ:

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника






Упаковка і розпаковування






Розмірні і посилальні типи

Перед розробниками CTS стояло завдання створення системи типів, де будь-яка суть була б об'єктом, але система типів при цьому працювала ефективно. Вони вирішили цю задачу, розділивши типи CTS на дві категорії: розмірні (value types) і посилальні (reference types). Як ви незабаром побачите, ці терміни відображають способи виділення пам'яті і внутрішнього функціонування змінних.

Розмірні типи

Якщо деяка змінна має розмірний тип, вона містить реальні дані. Отже, перше правило для розмірних типів таке: вони не можуть бути null. Нижче, наприклад, я на С# виділив пам'ять, створивши змінну типу System.Int32, який визначений в CTS. При цьому оголошенні відбувається не що інше, як виділення в стеку 32-розрядної області.

int i = 32;

Крім того, при присвоєнні значення у виділений простір поміщається 32-розрядне число.

У С# визначено декілька розмірних типів, включаючи перечислений (enumerators), структури (structures) і примітиви (primitives). Оголошуючи змінну одного з цих типів, ви кожного разу виділяєте в стеку деяке число байтів, що асоціюються з цим типом, і працюєте безпосередньо з виділеним масивом бітів. Крім того, коли ви передаєте змінну розмірного типу, передається значення змінної, а не посилання на лежачий в її основі об'єкт.

 

Посилальні типи

Посилання (якщо вона не рівна null) — це не просто адреса, яка, як ви вважаєте, може указувати (а може і не указувати) на певний об'єкт. Посилання завжди гарантовано указує об'єкт заданого типу, вже виділений в купі. Крім того, посилання може бути рівна null.

Нижче виділяється значення посилального типу (string), але при цьому «за кулісами» в купі виділяється значення і повертається посилання на нього:

string s = " Hello, World";

Як і у разі розмірних типів, в С# декілька типів визначено як посилальні: класи, масиви, делегати (delegates) і інтерфейси. Оголошуючи змінну одного з цих типів, ви кожного разу виділяєте в купі деяке асоційоване з цим типом число байт. Але замість того, щоб працювати з ними безпосередньо (як у разі розмірних типів), ви працюєте з посиланням на виділений об'єкт.

Упаковка і розпаковування

Як же ці різні категорії типів забезпечують ефективнішу роботу системи? Це робиться за допомогою упаковки (boxing). У простому випадку при упаковці розмірний тип перетвориться в посилальний. У зворотному випадку посилальний тип розпаковується (unbox) в розмірний.

Чудово в даній методиці те, що об'єкт лише тоді є об'єктом, коли це необхідно. Допустимо, ви оголошуєте змінну типу System.Int32. Для неї виділяється пам'ять в стеку. Ви можете передавати цю змінну будь-якому методу, визначеному як тип System, що приймає аргументи. Object, а також звертатися до будь-якого з її членів, до якого у вас є доступ. Тому ви сприймаєте і відчуваєте її як об'єкт. Але в реальності це всього 4 байти в стеку.

Тільки коли ви намагаєтеся використовувати цю змінну згідно правилам, певним інтерфейсом базового класу System.Object, система автоматично упаковує змінну, внаслідок чого вона стає посилальним типом і може бути використана так само, як будь-який об'єкт. Упаковка — це механізм, за допомогою якого в С# будь-яка суть може бути представлена у вигляді об'єкту. Це дозволяє уникнути витрат, неминучих в тому випадку, якщо б всяка суть насправді була об'єктом. Звернемося до прикладів:

int foo = 42; // Розмірний тип.

object bar = foo; // Змінна foo упакована в bar.

У першому рядку цього коду ми створювали змінну (foo) типу int. Як вам відомо, int є розмірним типом (оскільки це базисний тип). У другому рядку компілятор виявить, що змінна foo скопійована в посилальний тип, представлений змінною bar. При цьому компілятор додасть код MSIL, необхідний для упаковки цієї змінної.

А зараз виконаємо явне приведення типів, щоб перетворити bar назад в розмірний тип:

int foo = 42; // Розмірний тип.

object bar = foo; // Змінна foo упакована в bar.

int foo2 = (int)bar; // Розпаковування і приведення до типу int.

При упаковці (тобто перетворенні з розмірного типу в посилальний) явного приведення типів не потрібний. Проте при розпаковуванні — перетворенні з посилального типу в розмірний — приведення типів необхідне. Це так, тому що у разі розпаковування об'єкт може бути приведений до будь-якого типу. Перетворення дозволяє компілятору перевірити, чи можливе приведення для заданого типу змінної.

Корінь всіх типів: System.Object. Всі типи походять від типу System.Object, що дозволяє гарантувати наявність у кожного типу мінімального набору функціональних можливостей. Всі типи отримують «безкоштовно» чотири відкриті методи (табл. 6.1).

Табл. 6.1. Відкриті методи типу System.Object.

Метод Опис
bool Equals() Порівнює два посилання на об'єкти в період виконання, щоб визначити, чи указують вони в точності один і той же об'єкт. Якщо дві змінні посилаються на один і той же об'єкт, повертається true. У разі розмірних типів (див. про них наступний розділ) цей метод повертає true, якщо типи змінних ідентичні і їх значення рівні.
int GetHashCodeO Повертає заданий для об'єкту хзш-код. Хэш-функції використовуються в реалізації класу, коли хэш-код об'єкту потрібно помістити в хэш-таблицю для підвищення продуктивності.
Type GetType() Використовується з методами віддзеркалення для отримання інформації про тип даного об'єкту.
string ToString Використовується за умовчанням для отримання імені об'єкту. Його можна перевизначити в похідних класах, щоб вони повертали зрозуміле користувачеві текстове представлення об'єкту.

Нижче описані захищені методи System.Objeci (табл. 6.2).

Табл. 6.2. Захищені методи типу System.Object.

Метод Опис
void Finalize() Викликається в період виконання для звільнення ресурсів перед збором сміття. Цей метод можна викликати, а можна і не робити цього. Тому не поміщайте в нього що підлягає виконання код. Це правило виливається в щось під назвою детерміноване завершення (deterministic finalization).
Object MemberwiseClone Представляє обмежену копію (shallow сміттю) об'єкту. Під цим я розумію копію об'єкту, що містить посилання на інші об'єкти, але не копії цих об'єктів. Якщо ваші класи повинні підтримувати повну копію (deep сміттю), яка дійсно включає копії об'єктів, на які вона посилається, то вам потрібно реалізувати інтерфейс ICloneable і самому уручну проводити клонування або копіювання.

Поделиться с друзьями:

mylektsii.su - Мои Лекции - 2015-2024 год. (0.007 сек.)Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав Пожаловаться на материал