Реферат Введення ЄІАС у З#: класи

Страница 1 из 2 | Следующая страница

Вадим Бодров

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

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

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

ссылочные (reference types);

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

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

Интерфейсом в З# є тип посилань, у якому лише абстрактні елементи, які мають реалізації. Безпосередньо реалізація цих елементів має міститися у п'ятому класі, похідному від цього інтерфейсу (ви можете безпосередньо створювати екземпляри інтерфейсів). Интерфейсы З# можуть утримувати методи, властивості і индексаторы, та на відміну, наприклад, від 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. З іншого боку, члени інтерфейсів немає модифікаторів доступу. Їх область видимості визначається безпосередньо що реалізують класом.

Властивості

Розглядаючи класи мови З#, далебі не випадає обійти таке «нововведення», як властивості (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 і = 1; і < 8; і++)

{

Screen.Attributes = і;

Screen.PrintString(”Property Demo

”);

}

}

}

Програма виводить повідомлення «Property Demo», використовуючи різні кольору символів (від темно-синього до білого). Спробуймо з'ясувати, як працює. Отже, спочатку ми імпортуємо важливі нас функції API-интерфейса Windows: SetConsoleTextAttribute і GetStdHandle. На жаль, стандартний клас середовища .NETy під назвою 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.

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

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

Делегати

Мова програмування З# хоч і допускає, проте не заохочує використання покажчиків. У деяких ситуаціях буває особливо важко уникнути покажчиків на функції. Для цього в З# реалізовані звані делегати (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, призводить до послідовної виконання обох цих методів. Подібно застосуванню операції складання з об'єднання делегатів, можна використовувати й операцію вирахування, яка, як легко зрозуміти, виконує зворотне дію.

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

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

using System;

class Example_7

{

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

out double x1, out double x2)

{

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

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, де параметри завжди передаються за значенням.

Укладання

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

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

З# Language Specification. Microsoft Corporation, 2000.

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

Безплатна версія .NETy Framework SDK Beta 1: www.microsoft.com/downloads.

Обширнейшая інформація по платформі .NETy: www.gotdotnet.com.

Офіційна конференція з

Страница 1 из 2 | Следующая страница

Схожі реферати:

Навігація