Принципи при дизайна на езика C#
Преди да се запознаем със синтаксиса и програмните конструкции в C#, нека първо разгледаме основните принципи, залегнали при проектирането му.
Компонентно-ориентиран
Езикът C# е насочен към компонентно-ориентираното програмиране, при което софтуерът се изгражда чрез съединяване на различни готови компоненти и описание на логиката на взаимодействие между тях.
При проектирането на .NET Framework и езика C# компонентният подход е залегнал на най-дълбоко архитектурно ниво. .NET Framework дефинира общ компонентен модел, който установява правилата за изграждане и използване на компоненти за всички .NET приложения. Езикът C# поддържа класове, интерфейси, свойства, събития и други средства за описание на компонентите, както и средства за тяхното използване. В темата "Графичен потребителски интерфейс с Windows Forms" ще дискутираме по-задълбочено компонентния модел на .NET Framework.
Всички данни са обекти
С# е обектно-ориентиран език за програмиране. В него залягат основните принципи на обектно-ориентираното програмиране, като капсулация на данните, наследяване и полиморфизъм.
В .NET Framework всички типове данни наследяват системния тип System. Object и придобиват от него някои общи методи, свойства и други характеристики.
В следствие на това в C# всички данни се третират като обекти. Дори примитивните типове, чрез въвеждането на автоматичното им опаковане (boxing) и разопаковане (unboxing) се превръщат в обекти. Например, 5.ToString() е валидно извикване в C#, защото 5 се опакова и се разглежда като обект от тип System.Object, на който се извиква метода ToString().
Сигурност и надеждност на кода
По идея .NET Framework и C# са проектирани за да осигурят висока сигурност и надеждност на изпълнявания софтуер. .NET Framework предоставя среда за контролирано изпълнение на управляван код, с което прави невъзможно възникването на някои от най-неприятните проблеми, свързани с управлението на паметта, неправилното преобразуване на типове и др. C# наследява всички тези характеристики от .NET Framework и добавя към тях някои допълнителни механизми за предпазване на програмистите от често срещани грешки.
Силна типизираност и типова безопасност
С# е силно типизиран и типово обезопасен. В него не се използват указатели към паметта, които създават много проблеми в по-старите езици за програмиране. Вместо тях се използват специални силно типизирани указатели, които се наричат референции (references). Използването на референции вместо указатели решава проблемите, които възникват от неправилната работа с указатели и директния достъп до паметта. В .NET Framework управлението на паметта се извършва почти изцяло от CLR.
Всъщност в C# може да се използват указатели (като тези в C и C++) чрез запазената дума unsafe, но това не е се препоръчва в масовия случай, защото лишава програмата от типова обезопасеност и позволява неправилна работа с паметта.
В С# не може да се излезе от границите на масив или символен низ. При опит да бъде направено това, се получава изключение, което може да бъде прихванато и обработено. В езици като C, C++ и Pascal излизането от границите на масив води до достъп до памет, използвана от други данни и най-често пъти предизвиква сривове или неочаквано поведение на програмата.
При създаване на клас, структура или друг тип C# компилаторът не позволява да останат неинициализирани член-данни. Това защитава програмиста от възможността да работи с неинициализирани данни.
Макар С# да не инициализира автоматично локалните променливи, компилаторът предупреждава за неправилното им използване. Например следният код ще предизвика грешка при опит за компилация:
int value;
value = value + 5;
|
Преобразуването на типове също е безопасно. CLR не позволява да се извърши невалидно преобразуване на типове – да се преобразува променлива от даден тип към променлива от тип, който не е съвместим с първия. При опит да бъде направено това, възниква изключение.
Неявното преобразуване на типове е разрешено само за съвместими типове, когато не е възможна загуба на информация. При явно преобразуване на типове, ако те не са съвместими, се хвърля InvalidCastException по време на изпълнение. Например следният код предизвиква изключение по време на изпълнение:
object a = "This will raise InvalidCastException!";
int b = (int) a;
| Безопасност на аритметичните операции
В C# чрез запазената дума checked могат да се отделят блокове код, в които аритметичните операции се проверяват за препълване на типовете и ако това се случи, се хвърля OverflowException. Това е много полезно, защото за разлика от С++, където при такива ситуации се получава грешен резултат, в С# може да се реагира адекватно на такава специфична ситуация. Ето един пример, при който CLR засича препълване на типа int:
checked
{
int i = 100000;
int j = i*i; // OverflowException is thrown
}
| Експлицитно задаване на виртуални методи и припокриване на метод
Една от целите на езика C# е да позволи с малко усилия да се пише надежден код. С цел да се намалят грешките от припокриване на виртуални методи са въведени запазените думи new и override, чрез които да се контролира припокриването на виртуален метод, който е в базов клас при наследяване. Каква е разликата между двете? При полиморфизъм (обект от базовия клас е създаден като обект от наследника), ако е използвана new като модификатор на метода в наследника, ще се извика функцията на базовия клас, а при използване на override – функцията на наследника.
В .NET Framework заделянето и използването на паметта се управлява автоматично от CLR (Common Language Runtime). Стойностните типове, които ще разгледаме по-подробно в темата "Обща система от типове", се пазят в стека, докато референтните – в т. нар. "динамична памет" (managed heap), за която се грижи системата за почистване на паметта (garbage collector).
Системата за почистване на паметта е част от CLR и нейна задача е да освобождава периодично паметта и ресурсите, заделени за обекти, които не се използват повече от приложението. Такива обекти могат да бъдат най-разнообразни: данни в динамичната памет, масиви, символни низове, а също и файлове, буфери в паметта, връзки към бази данни и др.
Грижата за паметта е трудна и сложна задача, но благодарение на CLR тя не е задължение на .NET програмистите. На нея ще обърнем специално внимание в темата "Управление на паметта и ресурсите".
Широко използване на изключения
Обработката на грешки, които могат да възникнат по време на изпълнение на програмата, в .NET Framework се реализира чрез използване на изключения. Механизмът на изключенията позволява да се съобщи за възникнал проблем или неочаквана ситуация и за нея да може да се реагира адекватно.
Изключенията представляват обекти от клас Exception или производен на него клас и съдържат информация за възникналата грешка. Например, при опит за деление на нула CLR прихваща проблема и предизвиква изключението DivideByZeroException, а при опит за излизане от границите на масив възниква ArgumentOutOfRangeException. Работата с изключения също ще бъде дискутирана в детайли в темата "Управление на изключенията в .NET".
В .NET Framework са въведени т. нар. сигурност на ниво достъп до кода (code access security) и сигурност, базирана на роли (role-based security). Чрез тях се осъществява контрол на достъпа до ресурси от програмата. Например, ако трябва да се извика системна функция или да се пише във файл, кодът трябва да има права да го направи. Сигурността на ниво достъп до кода оставя CLR да взима решения, докато при сигурност, базирана на роли, програмата може да реагира различно спрямо ролята и правата на потребителя.
Всичкият код е на едно място
В С# няма разделяне на хедър файлове и файлове с имплементация, както в C и C++. Това спестява много проблеми и улеснява поддръжката на сорс кода. В C# всичкият програмен код на даден клас е в един файл.
Сподели с приятели: |