Введення в C класи

[ виправити ] текст може містити помилки, будь ласка перевіряйте перш ніж використовувати.

скачати

Вадим Бодров

Система класів грає важливу роль в сучасних мовах програмування. Як же вони реалізовані в новій мові C #, створеному корпорацією Microsoft, і навіщо потрібно вивчати С #?

Відповіді на ці питання залежать від того, як ви збираєтеся працювати далі. Якщо ви хочете створювати додатки для платформи. NET, то вам, швидше за все, не вдасться уникнути вивчення C #. Звичайно, можна використовувати і Сі + +, і Visual Basic або будь-яку мову програмування, тим більше що незалежними розробниками створюються транслятори з APL, Кобола, Eiffel, Haskell, Оберона, Smalltalk, Perl, Python, Паскаля та ін Однак для компілятора, здатного генерувати програми середовища. NET CLR (Common Language Runtime), тільки C # є «рідним» мовою. Він повністю відповідає ідеології. NET і дозволяє найбільш продуктивно працювати в середовищі CLR. У свій час для використання віртуальної машини Java було створено безліч так званих «перехідників» (bridges) c різних мов програмування, зокрема PERCobol, JPython, Eiffel-to-JavaVM System, Tcl / Java і т.д. Подібні розробки так і не одержали належного поширення. Практика показала, що значно простіше вивчити нову мову, ніж вводити додаткові розширення в менш підходящу для даних цілей систему програмування. І не треба бути провидцем, щоб стверджувати, що бо, більша частина програмістів, створюють додатки для платформи. NET, віддасть перевагу саме мови C #.

C # є мовою об'єктно-орієнтованого програмування, тому класи грають у ньому основну роль. Більш того, всі типи даних C #, як вбудовані, так і певні користувачем, породжені від базового класу object. Іншими словами, на відміну від Java, де примітивні типи даних відокремлені від об'єктних типів, всі типи даних в C # є класами і можуть бути розділені на дві групи:

посилальні (reference types);

звичайні (value types).

Зовні посилальні і звичайні типи дуже схожі, тому що аналогічно C + + в них можна оголошувати конструктори, поля, методи, оператори і т.д. Однак, на відміну від C + +, звичайні типи в C # не дозволяють визначати класи і не підтримують наслідування. Вони описуються за допомогою ключового слова struct і в основному використовуються для створення невеликих об'єктів. Посилальні ж типи описуються за допомогою ключового слова class і є покажчиками, а екземпляри таких типів посилаються на об'єкт, що знаходиться в купі (heap). Продемонструємо сказане на прикладі:

using System;

class CValue

{

public int val;

public CValue (int x) {val = x;}

}

class Example_1

{

public static void Main ()

{

CValue p1 = new CValue (1);

CValue p2 = p1;

Console.WriteLine ("p1 = {0}, p2 = {1}",

p1.val, p2.val);

p2.val = 2;

Console.WriteLine ("p1 = {0}, p2 = {1}",

p1.val, p2.val);

}

}

Відкомпілювати і виконавши програму, отримаємо наступний результат:

p1 = 1, p2 = 1

p1 = 2, p2 = 2

Як неважко бачити, p2 є всього лише посиланням на p1. Тим самим стає очевидно, що при зміні поля val екземпляра класу p2 насправді змінюється значення відповідного поля p1. Подібний підхід не дуже зручний при роботі з примітивними типами даних, які повинні містити саме значення, а не посилання на нього (Complex, Point, Rect, FileInfo і т.д.). Для опису таких об'єктів і призначені типи значень:

using System;

struct SValue

{

public int val;

public SValue (int x) {val = x;}

}

class Example_2

{

public static void Main ()

{

SValue p1 = new SValue (1);

SValue p2 = p1;

Console.WriteLine ("p1 = {0}, p2 = {1}",

p1.val, p2.val);

p2.val = 2;

Console.WriteLine ("p1 = {0}, p2 = {1}",

p1.val, p2.val);

}

}

Ось що вийде після запуску вищенаведеної програми:

p1 = 1, p2 = 1

p1 = 1, p2 = 2

З цього випливає, що екземпляр класу p2 є самостійним об'єктом, який містить власне поле val, не пов'язане з p1. Використання звичайних типів дозволяє уникнути додаткового витрачання пам'яті, оскільки не створюються додаткові посилання, як у випадку з примірниками класів. Звичайно, економія невелика, якщо у вас є всього кілька невеликих об'єктів типу Complex або Point. Зате для масиву, що містить кілька тисяч таких елементів, картина може докорінно змінитися. У таблиці наведені основні відмінності типів class і struct.

Інтерфейси

Класи у мові C # зазнали досить серйозні зміни в порівнянні з мовою програмування C + +, який і був взятий за основу. Перше, що кидається в очі, це неможливість множинного успадкування. Такий підхід вже знайомий тим, хто пише на мовах Object Pascal і Java, а от програмісти C + + можуть бути дещо спантеличені. Хоча при більш близькому розгляді дане обмеження вже не здається скільки-небудь серйозних або непродуманим. По-перше, множинне спадкування, реалізоване в C + +, нерідко було причиною нетривіальних помилок. (При тому що не так вже й часто доводиться описувати класи за допомогою множинного успадкування.) По-друге, в C #, як і в діалекті Object Pascal фірми Borland, дозволено успадкування від кількох інтерфейсів.

Інтерфейсом в C # є тип посилань, що містить тільки абстрактні елементи, що не мають реалізації. Безпосередньо реалізація цих елементів повинна міститися в класі, похідному від даного інтерфейсу (ви не можете безпосередньо створювати екземпляри інтерфейсів). Інтерфейси C # можуть містити методи, властивості і індексатори, але на відміну, наприклад, від Java, вони не можуть містити константних значень. Розглянемо найпростіший приклад використання інтерфейсів:

using System;

class CShape

{

bool IsShape () {return true;}

}

interface IShape

{

double Square ();

}

class CRectangle: CShape, IShape

{

double width;

double height;

public CRectangle (double width, double height)

{

this.width = width;

this.height = height;

}

public double Square ()

{

return (width * height);

}

}

class CCircle: CShape, IShape

{

double radius;

public CCircle (double radius)

{

this.radius = radius;

}

public double Square ()

{

return (Math.PI * radius * radius);

}

}

class Example_3

{

public static void Main ()

{

CRectangle rect = new CRectangle (3, 4);

CCircle circ = new CCircle (5);

Console.WriteLine (rect.Square ());

Console.WriteLine (circ.Square ());

}

}

Обидва об'єкти, rect і circ, є похідними від базового класу CShape і тим самим вони успадковують єдиний метод IsShape (). Задавши ім'я інтерфейсу IShape в оголошеннях CRectangle і CCircle, ми вказуємо на те, що в даних класах міститься реалізація всіх методів інтерфейсу IShape. Крім того, члени інтерфейсів не мають модифікаторів доступу. Їх область видимості визначається безпосередньо реалізують класом.

Властивості

Розглядаючи класи мови C #, просто не можна обійти таке «нововведення», як властивості (properties). Треба сказати, що тут відчувається вплив мов Object Pascal і Java, в яких властивості завжди були невід'ємною частиною класів. Що ж являють собою ці самі властивості? З точки зору користувача, властивості виглядають практично так само, як і звичайні поля класу. Їм можна присвоювати деякі значення і отримувати їх назад. У той же час властивості мають бо, більшу функціональність, так як читання і зміна їх значень виконується за допомогою спеціальних методів класу. Такий підхід дозволяє ізолювати користувача модель класу від її реалізації. Пояснимо це визначення на конкретному прикладі:

using System;

using System.Runtime.InteropServices;

class Screen

{

[DllImport ("kernel32.dll")]

static extern bool SetConsoleTextAttribute (

int hConsoleOutput, ushort wAttributes

);

[DllImport ("kernel32.dll")]

static extern int GetStdHandle (

uint nStdHandle

);

const uint STD_OUTPUT_HANDLE = 0x0FFFFFFF5;

static Screen ()

{

output_handle = GetStdHandle (STD_OUTPUT_HANDLE);

m_attributes = 7;

}

public static void PrintString (string str)

{

Console.Write (str);

}

public static ushort Attributes

{

get

{

return m_attributes;

}

set

{

m_attributes = value;

SetConsoleTextAttribute (output_handle, value);

}

}

private static ushort m_attributes;

private static int output_handle;

}

class Example_4

{

public static void Main ()

{

for (ushort i = 1; i <8; i + +)

{

Screen.Attributes = i;

Screen.PrintString ("Property Demo

");

}

}

}

Програма виводить повідомлення «Property Demo», використовуючи різні кольори символів (від темно-синього до білого). Давайте спробуємо розібратися в тому, як вона працює. Отже, спочатку ми імпортуємо важливі для нас функції API-інтерфейсу Windows: SetConsoleTextAttribute і GetStdHandle. На жаль, стандартний клас середовища. NET під назвою Console не має коштів управління кольором виводу текстової інформації. Треба думати, що корпорація Microsoft в майбутньому все-таки вирішить цю проблему. Поки ж для цих цілей доведеться скористатися службою виклику платформи PInvoke (зверніть увагу на використання атрибуту DllImport). Далі, в конструкторі класу Screen ми отримуємо стандартний дескриптор потоку виведення консольного застосування і розміщуємо його значення в закриту змінну output_handle для подальшого використання функцією SetConsoleTextAttribute. Крім цього, ми присвоюємо іншої змінної m_attributes початкове значення атрибутів екрану (7 відповідає білому кольору символів на чорному тлі). Зауважимо, що в реальних умовах варто було б отримати поточні атрибути екрана за допомогою функції GetConsoleScreenBufferInfo з набору API-інтерфейсу Windows. У нашому ж випадку це дещо ускладнило б приклад і відволікло від основної теми.

У класі Screen ми оголосили властивість Attributes, для якого визначили функцію читання (getter) і функцію запису (setter). Функція читання не виконує яких-небудь специфічних дій і просто повертає значення поля m_attributes (у реальному програмі вона мала б повертати значення атрибутів, отримане за допомогою все тієї ж GetConsoleScreenBufferInfo). Функція запису трохи складніше, тому що крім тривіального поновлення значення m_attributes вона викликає функцію SetConsoleTextAttribute, встановлюючи задані атрибути функцій виведення тексту. Значення встановлюються атрибутів передається спеціальної змінної value. Зверніть увагу на те, що поле m_attributes є закритим, а отже, воно не може бути доступно поза класом Screen. Єдиним способом читання та / або зміни цього методу є властивість Attributes.

Властивості дозволяють не тільки повертати і змінювати значення внутрішньої змінної класу, але й виконувати додаткові функції. Так, вони дозволяють зробити перевірку значення або виконати інші дії, як показано в наведеному вище прикладі.

У мові C # властивості реалізовані на рівні синтаксису. Більш того, рекомендується взагалі не використовувати відкритих полів класів. На перший погляд, при такому підході втрачається ефективність через те, що операції привласнення будуть замінені викликами функцій getter і setter. Аж ніяк! Середа. NET згенерує для них відповідний inline-код.

Делегати

Мова програмування C # хоча і допускає, але все ж не заохочує використання покажчиків. У деяких ситуаціях буває особливо важко обійтися без покажчиків на функції. Для цих цілей в C # реалізовані так звані делегати (delegates), які іноді ще називають безпечними аналогами покажчиків на функцію. Нижче наведено простий приклад використання методу-делегати:

using System;

delegate void MyDelegate ();

class Example_5

{

static void Func ()

{

System.Console.WriteLine («MyDelegate.Func ()»);

}

public static void Main ()

{

MyDelegate f = new MyDelegate (Func);

f ();

}

}

Крім того, що делегати забезпечують типову захищеність, а отже, і підвищують безпеку коду, вони відрізняються від звичайних покажчиків на функції ще й тим, що є об'єктами, похідними від базового типу System.Delegate. Таким чином, якщо ми використовуємо делегат для вказівки на статичний метод класу, то він просто зв'язується з відповідним методом даного класу. Якщо ж делегат вказує на нестатичні метод класу, він зв'язується вже з методом екземпляри такого класу. Це дозволяє уникнути порушення принципів ООП, оскільки методи не можуть бути використані окремо від класу (об'єкта), в якому вони визначені.

Ще однією відмінністю делегатів від простих покажчиків на функції є можливість виклику декількох методів з допомогою одного делегата. Розглянемо це на конкретному прикладі:

using System;

delegate void MyDelegate (string message);

class Example_6

{

public static void Func1 (string message)

{

Console.WriteLine ("{0}: MyDelegate.Func1", message);

}

public static void Func2 (string message)

{

Console.WriteLine ("{0}: MyDelegate.Func2", message);

}

public static void Main ()

{

MyDelegate f1, f2, f3;

f1 = new MyDelegate (Func1);

f2 = new MyDelegate (Func2);

f3 = f1 + f2;

f1 ("Calling delegate f1");

f2 ("Calling delegate f2");

f3 ("Calling delegate f3");

}

}

Відкомпілювати і виконавши вищенаведену програму, отримаємо наступний результат:

Calling delegate f1: MyDelegate.Func1

Calling delegate f2: MyDelegate.Func2

Calling delegate f3: MyDelegate.Func1

Calling delegate f3: MyDelegate.Func2

З цього випливає, що виклик методу-делегата f3, отриманого за допомогою операції складання f1 + f2, призводить до послідовного виконання обох цих методів. Подібно застосування операції складання з метою об'єднання делегатів, можна використовувати і операцію віднімання, яка, як неважко здогадатися, виконує зворотну дію.

Способи передачі параметрів

Аналізуючи особливості реалізації класів мови C #, хотілося б приділити увагу і способам передачі параметрів методу по посиланню. Іноді виникає потреба в тому, щоб функція повертала відразу кілька значень. Розглянемо це на прикладі програми, що обчислює квадратний корінь:

using System;

class Example_7

{

static int GetRoots (double a, double b, double c,

out double x1, out double x2)

{

double d = b * b - 4 * a * c;

if (d> 0)

{

x1 = - (b + Math.Sqrt (d)) / (2 * a);

x2 = - (b - Math.Sqrt (d)) / (2 * a);

return 2;

} Else

if (d == 0)

{

x1 = x2 =-b / (2 * a);

return 1;

} Else

{

x1 = x2 = 0;

return 0;

}

}

public static void Main ()

{

double x1, x2;

int roots = GetRoots (3, -2, -5, out x1, out x2);

Console.WriteLine ("roots #: {0}", roots);

if (roots == 2)

Console.WriteLine ("x1 = {0}, x2 = {1}", x1, x2);

else

if (roots == 1)

Console.WriteLine ("x = {0}", x1);

}

}

Щоб функція GetRoots повертала обидва кореня рівняння (x1 та x2), ми вказали транслятору, що змінні x1 і x2 повинні бути передані по посиланню, застосувавши для цього параметр out. Зверніть увагу на те, що нам не обов'язково ініціалізувати змінні x1 і x2 перед викликом функції GetRoots. Позначивши функцію ключовим словом out, ми доб'ємося того, що її аргументи можуть використовуватися тільки для повернення якогось значення, але не для його передачі всередину функції. Таким чином, мається на увазі, що змінна буде ініціалізований в тілі самої функції. У разі ж, якщо нам з якоїсь причини буде потрібно передати в параметрі функції деяке значення з можливістю його подальшого зміни, можна скористатися параметром ref. Дія цього параметра дуже схоже на дію out, але він дозволяє ще й передавати значення параметра тілу функції. Друга відмінність ключового слова ref полягає в тому, що передається параметр функції повинен бути инициализирован заздалегідь.

Такий метод дуже нагадує використання параметра var у списку аргументів функцій, прийняте в мові програмування Паскаль, і є ще однією відмінністю від мови Java, де параметри завжди передаються за значенням.

Висновок

Мова програмування C #, як і платформа. NET, знаходиться в розвитку. Зокрема, найближчим часом можна чекати появи узагальнених шаблонів, які подібно шаблонам мови програмування С + + дозволять створювати сильно типізовані класи-колекції. У будь-якому випадку мова програмування C # вже цілком сформувався для того, щоб його вивчити і почати застосовувати в реальних додатках.

Список літератури

C # Language Specification. Microsoft Corporation, 2000.

Гуннерсон Е. Введення в C #. СПб.: Питер, 2001.

Безкоштовна версія. NET Framework SDK Beta 1: www.microsoft.com / downloads.

Широка інформація по платформі. NET: www.gotdotnet.com.

Офіційна конференція з мови C #: news: / / msnews.microsoft.com / microsoft.public.dotnet.languages.csharp.

Інструментарій С #

Перш ніж почати роботу з мовою програмування C #, необхідно встановити на комп'ютері набір інструментальних засобів під назвою. Net Framework SDK, бета-версія якого доступна для безкоштовного завантаження безпосередньо c Web-сторінки корпорації Microsoft [3]. Крім того, знадобиться хороший текстовий редактор, що підтримує синтаксично настроюється орієнтований режим (syntax highlight) і дозволяє виділяти ключові слова в вихідних текстах тієї чи іншої мови програмування. Я рекомендую програму SharpDevelop (www.icsharpcode.net), поширювану незалежними програмістами на умовах ліцензії GNU. У крайньому випадку можна використовувати будь-який редактор, здатний працювати з вихідними текстами на мові програмування С / C + +, або навіть звичайний текстовий редактор Notepad.

Основні відмінності типів struct і class
Тип class Тип struct
Представлення примірника типу покажчик значення
Місце розташування об'єкта купа стек
Значення за замовчуванням null заповнюється нулями
Результат операції присвоювання для екземплярів типу копіюється покажчик копіюється сам об'єкт
Базовий тип вбудований тип string вбудований тип int

C # і Java

Мова програмування C # часто і небезпідставно порівнюють з Java. Обидві мови були створені для аналогічних цілей і мають багато спільного, зокрема синтаксис, що базується на C + +. У той же час є й безліч відмінностей, що відносяться до базових типами, класами, способів передачі параметрів, реалізації інтерфейсів і т. д. Основним же відмінністю між C # і Java є те, що Java-додатки працюють із середовищем Java Frameworks and Runtime, а C #-програми - з середовищем. NET Framework and Runtime. У повному обсязі концепція. NET буде реалізована тільки в новій операційній системі Windows XP (також відома як Whistler), хоча вона вже близько року активно просувається корпорацією Microsoft. Схоже, якщо ви плануєте створювати додатки, сумісні з платформою Microsoft, явно варто ближче познайомитися з Microsoft. NET. Кращим же мовою для створення. NET-додатків, за твердженням самої корпорації Microsoft, є C #.

Від двох до ...

Оригінальний текст будь-якого виконуваного програми, написаної на мові програмування C #, містить статичний метод Main (), - аналог знайомої програмістам Сі / Сі + + функції main (). Саме з цього методу починається виконання програми.

Що ж станеться, якщо вихідний текст буде містити два або більше методів Main (), як показано нижче?

using System;

class SayHello

{

public static void Main ()

{

Console.WriteLine ("Hello friend!");

}

}

class SayBye

{

public static void Main ()

{

Console.WriteLine ("Bye, bye ...");

}

}

Зрозуміло, компіляція цього прикладу викличе повідомлення про помилку, так як класи SayHello і SayBye абсолютно «рівноправні» з точки зору транслятора. Процес компіляції буде перерваний. Однак існує спеціальний ключ компілятора / main, за допомогою якого можна вказати клас, який містить потрібний нам метод Main (). Вищенаведений приклад, відкомпільований з ключем / main: SayHello, надрукує повідомлення:

Hello friend!

Якщо ж відкомпілювати той же самий приклад, вказавши ключ / main: SayBye, то текст буде іншим:

Bye, bye ...

Додати в блог або на сайт

Цей текст може містити помилки.

Програмування, комп'ютери, інформатика і кібернетика | Реферат
35.2кб. | скачати


Схожі роботи:
Класи і страти
Успадкування та класи-колекції
Класи в російському суспільстві
Успадкування та класи колекції
Класи і масову свідомість
Класи і класова система
Фактор групи Cмежние класи
Класи і партії в жовтні 1917 року
Розвиток груп Класи організаційних структур
© Усі права захищені
написати до нас