2. Въведение в. Net среда net framework архитектура. Поддържане на единна езикова среда


Работа с директории. Класове Directory и DirectoryInfo



страница2/4
Дата07.08.2017
Размер0.51 Mb.
#27470
1   2   3   4

Работа с директории. Класове Directory и DirectoryInfo

Класовете Directory и DirectoryInfo са помощни класове за работа с директории. Ще изброим основните им методи, като отбележим, че за Directory те са статични, а за DirectoryInfo – достъпни чрез инстанция.

- Create(), CreateSubdirectory() – създава директория или подди-ректория.

- GetFiles(…) – връща всички файлове в директорията.

- GetDirectories(…) – връща всички поддиректории на директорията.

- MoveTo(…) – премества (преименува) директория.

- Delete() – изтрива директория.

- Exists() – проверява директория дали съществува.

- Parent – връща горната директория.

- FullName – пълно име на директорията.



20. Таймер (Timer). Работа с таймери. Пропъртита, събития. Пример.

Таймери

Често в приложенията, които разработваме, възниква необходимост от

изпълняване на задачи през регулярни времеви интервали. Таймерите

предоставят такава услуга. Те са обекти, които известяват приложението

при изтичане на предварително зададен интервал от време. Таймерите са

полезни в редица сценарии, например, когато искаме да обновяваме

периодично потребителския интерфейс с актуална информация за статуса

на някаква задача или да проверяваме състоянието на променящи се

данни.

System.Timers.Timer

Класът предоставя събитие за изтичане на времевия интервал Elapsed,

което е делегат от тип ElapsedEventHandler, дефиниран като:

public delegate void ElapsedEventHandler(

object sender, ElapsedEventArgs e);

При изтичане на интервала, указан в свойството Interval, таймерът от

тип System.Timers.Timer ще извика записалите се за събитието методи,

използвайки нишка от пула. Ако използваме един и същ метод за

получаване на събития от няколко таймера, чрез аргумента sender можем

да ги разграничим. Класът ElapsedEventArgs чрез свойството DateTime



SignalTime ни предоставя точното време, когато е бил извикван метода.

За стартиране и спиране на известяването, можем да извикаме съответно



Start() и Stop() методите. Свойството Enabled ни позволява да инструк-

тираме таймера да игнорира събитието Elapsed. Това прави Enabled

функционално еквивалентно на съответните Start() и Stop() методи.

Когато приключим с таймера, трябва да извикаме Close(), за да осво-

бодим съответните системни ресурси.

System.Threading.Timer

System.Threading.Timer прилича на System.Timers.Timer и също

използва пула с нишки. Основната разлика е, че той позволява малко по-

разширен контрол – може да указваме кога таймера да започне да

отброява, както и да предаваме всякаква информация на метода за

обратни извиквания чрез обект от произволен тип. За да ползваме

System.Threading.Timer, трябва в конструктора му да подадем делегат от

тип TimerCallback, дефиниран като:

public delegate void TimerCallback(object state);

При всяко изтичане на времевия интервал, ще бъдат извиквани методите

в този делегат. Обикновено като обект за състояние има полза да

подаваме създателя на таймера, за да можем да използваме същия метод за обратни извиквания за обработка на събития от множество таймери.

Другият параметър в конструктора на таймера е времевият интервал. Той

може и да бъде променен впоследствие с извикване на Change(…) метода.



System.Threading.Timer не предлага удобен начин за стартиране и

спиране. Неговата работа започва веднага след конструирането му (по-

точно след изтичането на подаденото стартово време) и прекъсването му

става само чрез Dispose(). Ако искаме да го рестартираме трябва да

създадем нов обект.

System.Windows.Forms.Timer

Пространството от имена System.Windows.Forms съдържа още един клас

за таймер, който е със следната дефиниция:

public class Timer : Component, IComponent, Idisposable

{

public Timer();



public bool Enabled{virtual get ; virtual set;}

public int Interval {get; set;}

public event EventHandler Tick;

public void Start();

public void Stop();

}

Въпреки, че методите на System.Windows.Forms.Timer много приличат на



тези на System.Timers.Timer, то System.Windows.Forms.Timer не изпол-

зва пула с нишки за обратните извиквания към Windows Forms приложе-

нието. Вместо това, през определено време той пуска Windows съобще-

нието WM_TIMER в опашката за съобщения на текущата нишка.

Използването на System.Windows.Forms.Timer се различава от употребата

на System.Timers.Timer, само по сигнатурата на делегата за обратни

извиквания, който в случая е стандартният EventHandler.

26.Изключения в .NET . Дефиниране на собствено изключение.

Собствени изключения

В .NET Framework програмистите могат да дефинират собствени класове за изключения и да създават класови йерархии с тях. Това осигурява много голяма гъвкавост при управлението на грешки и необичайни ситу- ации. В по-големите приложения изключенията се разделят в логически в категории и за всяка категория се дефинира по един базов клас, а за конкретните представители на категориите се дефинира по един клас- наследник. Създава се по един абстрактен базов клас за категорията изключения, свързани с клиентите (CustomerException) и за категорията изключения, свързани с поръчките (OrderException). Наследниците на OrderException и CustomerException също могат да се подреждат в класова йерархия и да дефинират собствени подкатегории.

При работата на приложението, използващо класовата йерархия от примера могат да се прихващат наведнъж всички грешки, свързани с клиентите или само някои конкретни от тях. Това дава добра гъвкавост при управлението на грешките.

Добре е да се спазва правилото, че йерархиите трябва да са широки и плитки, т.е. класовете на изключения трябва да са производни на тип, който се намира близо до System.Exception, и трябва да бъдат не повече от две или три нива надълбоко. Ако дефинираме тип за изключение, който няма да бъде базов за други типове, маркираме го като sealed, а ако не искаме да бъде инстанциран директно, го правим абстрактен.Дефиниране на собствени изключения За дефинирането на собствени изключения се наследява класът System. ApplicationException и му се създават подходящи конструктори и евен-туално му се добавят и допълнителни свойства, даващи специфична информация за проблема. Препоръчва се винаги да се дефинират поне следните два конструктора:

MyException(string message);

MyException(string message, Exception InnerException);


27.Правила за работа с изключения в .NET среда

Правила за работа с изключения

1. разработвате библиотека: ако прихванете всички изкл. , как

разработващия приложение с библиотеката ще знае че нещо се е случило

2. разработвате библиотека с типове – не винаги знаете кое е грешка, кое не. Оставете това на викащия

3. Избягвайте код , прихващащ всичко: catch(System.Exception) {………}

4. Ако операция е частично завършена изключение и следва възстановяване в начално съст.: най-добре прихванете всичко, възстановете и уведомете (с друго изкл. ) викащата страна.

5. След прихващане и обработка на изключение, често е добре да уведомите извикващия: подавате същото (само с throw) или друго изключение (това е начина за преобразуваме изключението от нещо специфично, към общоразбираемо за потребител).


Необработвани съобщения (такива, които никой catch не разпознава)

Най напред следва да се разработи единна политика за тях – напр. въведен текст се съхранява и се визуализира диалогов прозорец с информация и т.н.;

-1.При отдалечено викана процедура или web услуга или сървърно-базиран код, който подава exception, то той се изпълнява в сървърно обкръжение на try/catch. Тъи като exception обекта е сериализиран, той може да се предава през граница на Domain – т.е. обратно към клиентското приложение.

-2. В общия случай, необработени съобщения могат да се насочват за обработка към дефинирана в в приложението делегатна функция, регистрирана като

event handle от тип System.UnhandledExceptionEventHandler към стандартния тип за изключния: System.AppDomain.UnhandledException . Пример:

AppDomain.CurentDomain.UnhandledException +=

new UnhandledExceptionEventHandler(MyUnhandledExceptionFunction);

3. Необработваните изключения в приложения, базирани на Windows Forms се прихващат така: цялата WinProc ф-ия всъщност, се вика в обхващащ я автоматично try/catch.

При наличие на необработено по-долу изключение, catch блокът извиква виртуалния метод OnThreadException() дефиниран в System.Windows.Forms.Control и предефиниран в Application

Той визуализира стандартен прозорец за ‘unhandled exception’

Можете да предефинирате поведението чрез ваш метод от делегатен тип

System.Threading.ThreadExceptionEventHandler

и след това да свържете този метод с ThreadException събитието на класа Application

-4. Необработени съобщения в ASP.NET

ASP обхваща кода на приложението в собствен try блок и предопределя начин за обработка. Може да се намесите като регистрирате свой callback метод към събитие Error на класа System.Web.UI.Page или на клас System.Web.UI.UserControl

(методът може и да се вика за всяко необработено изключение от която и да е страница на

приложението – ако callback метода е свързан с Error събитие на клас

System.Web.HTTPApplication)

5. Необработени изключения в среда ASP.NET XML

Отново обхващащ кода try блок на ASP.NET подава SoapException обект. Той се сериализира в

XML вид и може да се предава към друг компютър или приложение, работещо като клиент на XML Web услугата.

30. Същност на механизма на сериализация. Сериализиране на обекти с вградени класове.

Сериализация

В съвременното програмиране често се налага да се съхрани състоянието

на даден обект от паметта и да се възстанови след известно време. Това

позволява обектите временно да се съхраняват на твърдия диск и да се

използват след време, както и да се пренасят по мрежата и да се възста-

новяват на отдалечена машина.

Проблемите при съхранението и възстановяването на обекти са много и за

справянето с тях има различни подходи. За да се намалят усилията на

разработчиците в .NET Framework е изградена технология за автомати-

зация на този процес, наречена сериализация. Нека се запознаем по-

подробно с нея.

Какво е сериализация (serialization)?

Сериализацията е процес, който преобразува обект или свързан граф от

обекти до поток от байтове, като запазва състоянието на неговите полета

и свойства. Потокът може да бъде двоичен (binary) или текстов (XML).



Запазване на състоянието на обект

Сериализацията се използва за съхранение на информация и запазване

на състоянието на обекти. Използвайки сериализация, дадена програма

може да съхрани състоянието си във файл, база данни или друг носител и

след време да го възстанови обратно.

можем да сериализираме обект

и да го запишем в бинарен файл със средствата на .NET Framework:

string str = ".NET Framework";

BinaryFormatter f = new BinaryFormatter();

using (Stream s = new FileStream("sample.bin", FileMode.Create))

{

f.Serialize(s, str);



}

При сериализирането на обекта в потока се записват името на класа, име-

то на асемблито (assembly) и друга информация за обекта, както и всички

член-променливи, които не са маркирани като [NonSerialized] (употре-

бата на този атрибут ще обясним по-нататък в тази тема). При десериали-

зацията информацията се чете от потока и се пресъздава обектът.



Методи за сериализация

public static MemberInfo[] GetSerializableMembers(Type)

Методът приема като параметър типа на класа, който ще бъде сериали-

зиран, и връща като резултат масив от MemberInfo обекти, съдържащи

информация за сериализируемите членове на класа.



public static Object[] GetObjectData(Object, MemberInfo[])

Методът приема като параметри обект, който ще бъде сериализиран и

масив с членовете, които трябва бъдат извлечени от обекта. За всеки от

тях се извлича стойността, асоциирана с него в сериализирания обект и

тези стойности се връщат като масив от обекти. Дължината му е същата,

като дължината на масива с членовете, извличани от обекта.



35. Стратегии на управление на памет и събиране на ‘боклук’ в .NET среда. Алгоритъм за “ събиране

на боклук

Как работи garbage collector?

Вече беше споменато, че ако добавянето на нов обект би довело до препълване на хийпа, трябва да се

осъществи почистване на паметта. В този момент, CLR стартира системата за почистване на паметта, т.нар. garbage collector. Всъщност това е опростено обяснение. Garbage collector се

стартира когато Поколение 0 се запълни. Поколенията се разглеждат в следващата секция.

Първото нещо, което трябва да се направи, за да може системата за почистване на паметта да започне работа, това е да се приспят всички нишки на приложението, изпълняващи управляван код. по време на събирането на отпадъци е твърде вероятно обектите да се преместят на нови адреси в динамичната памет, нишките не трябва да могат да достъпват и модифицират обекти докато трае почистването. CLR изчаква всички нишки да достигнат в безопасно състояние, след което ги приспива. Съществуват няколко механизма, чрез които CLR може да приспи дадена нишка. Причината за тези различни механизми е

стремежът да се намали колкото се може повече натоварването и нишките да останат активни възможно най-дълго.

Освобождаване на неизползваните обекти

След като всички управлявани нишки на приложението са безопасно "приспани", garbage collector проверява дали в managed heap има обекти, които вече не се използват от приложението. Ако такива обекти съществуват, заетата от тях памет се освобождава. След приключване на

работата по събиране на отпадъци се възобновява работата на всички

нишки и приложението продължава своето изпълнение. Както вероятно се досещате, откриването на ненужните обекти и освобождаването на ресурсите, заети от тях, не е проста задача. В тази

секция накратко ще опишем алгоритъмът, който .NET garbage collector използва за нейното решаване.

За да установи кои обекти подлежат на унищожение, garbage collector построява граф на всички обекти, достъпни от нишките на приложението в дадения момент. Всички обекти от динамичната памет, които не са част от графа се считат за отпадъци и подлежат на унищожаване. Възниква въпросът как garbage collector може да знае кои обекти са достъпни и кои не? Корените на приложението са точката, от която

системата за почистване на паметта започва своята работа.

Корени на приложението

Всяко приложение има набор от корени (application roots). Корените представляват области от паметта, които сочат към обекти от managed heap, или са установени на null. Например всички глобални и статични

променливи, съдържащи референции към обекти се считат за корени на приложението. Всички локални променливи или параметри в стека към момента, в който се изпълнява garbage collector, които сочат към обекти, също принадлежат към корените. Регистрите на процесора, съдържащи указатели към обекти, също са част от корените. Към корените на приложението спада и Freachable queue (за Freachable queue по-подробно ще стане дума в секцията за финализация на обекти в настоящата глава.

Засега просто приемете че тази опашка е част от вътрешните структури, поддържани от CLR и се счита за един от корените на приложението). Когато JIT компилаторът компилира IL инструкциите на даден метод в

процесорни инструкции, той също съставя и вътрешна таблица, съдър- жаща корените за съответния метод. Тази таблица е достъпна за garbage collector. Ако се случи garbage collector да започне работа, когато методът се изпълнява, той ще използва тази таблица, за да определи кои са

корените на приложението към този момент. Освен това се обхожда и стекът на извикванията за съответната нишка и се определят корените зa всички извикващи методи (като се използват техните вътрешни таблици). Към получения набор от корени, естествено, се включват и тези, намира-

щи се в глобални и статични променливи.Трябва да се помни, че не е задължително даден обект да излезе от обхват за да бъде считан за отпадък. JIT компилаторът може да определи__

кога този обект се достъпва от кода за последен път и веднага след това

го изключва от вътрешната таблица на корените, с което той става

кандидат за почистване от garbage collector. Изключение правят случаите,

когато кодът е компилиран с /debug опция, която предотвратява почист-

ването на обекти, които са в обхват. Това се прави за улеснение на

процеса на дебъгване – все пак при трасиране на кода бихме искали да

можем да следим състоянието на всички обекти, които са в обхват в

дадения момент.

Алгоритъмът за почистване на паметта

Когато garbage collector започва своята работа, той предполага че всички

обекти в managed heap са отпадъци, т.е. че никой от корените не сочи

към обект от паметта. След това, системата за почистване на паметта

започва да обхожда корените на приложението и да строи граф на

обектите, достъпни от тях.

Нека разгледаме примера, показан на следващата фигура. Ако глобална

променлива сочи към обект A от managed heap, то A ще се добави към

графа. Ако A съдържа указател към C, а той от своя страна към обектите

D и F, всички те също стават част от графа. Така garbage collector обхожда

рекурсивно в дълбочина всички обекти, достъпни от глобалната промен-

лива A:


Когато приключи с построяването на този клон от графа, garbage collector

преминава към следващия корен и обхожда всички достъпни от него

обекти. В нашия случай към графа ще бъде добавен обект E. Ако по време

на работата garbage collector се опита да добави към графа обект, който

вече е бил добавен, той спира обхождането на тази част от клона. Това се

прави с две цели:

- значително се увеличава производителността, тъй като не се преми-

нава през даден набор от обекти повече от веднъж;

- предотвратява се попадането в безкраен цикъл, ако съществуват

циклично свързани обекти (например A сочи към B, B към C, C към D

и D обратно към A).

След обхождането на всички корени на приложението, Графът съдържа

всички обекти, които по някакъв начин са достъпни от приложението. В

посочения на фигурата пример, това са обектите A, C, D, E и F.

Всички обекти, които не са част от този граф, не са достъпни и следова-

телно се считат за отпадъци. В нашия пример това са обектите B, G, H и I.

След идентифицирането на достъпните от приложението обекти, garbage

collector преминава през хийпа, търсейки последователни блокове от

отпадъци, които вече се смятат за свободно пространство. Когато такава

област се намери, всички обекти, намиращи се над нея се придвижват

надолу в паметта, като се използва стандартната функция memcpy(…).

Крайният резултат е, че всички обекти, оцелели при преминаването на

garbage collector, се разполагат в долната част на хийпа, а NextObjPtr се

установява непосредствено след последния обект. Фигурата показва със-

тоянието на динамичната памет след приключване на работата на garbage

collector.



Поколения памет

Поколенията (generations) са механизъм в garbage collector, чиято

единствена цел е подобряването на производителността. Основната идея

е, че почистването на част от динамичната памет винаги е по-бързо от

почистването на цялата памет. Вместо да обхожда всички обекти от

хийпа, garbage collector обхожда само част от тях, класифицирайки ги по

определен признак. В основата на механизма на поколенията стоят

следните предположения:

- колкото по-нов е един обект, толкова по-вероятно е животът му да е

кратък. Типичен пример за такъв случай са локалните променливи,

които се създават в тялото на даден метод и излизат от обхват при

неговото напускане.

- колкото по-стар е обектът, толкова по-големи са очакванията той да

живее дълго. Пример за такива обекти са глобалните променливи.

- обектите, създадени по едно и също време обикновено имат връзка

помежду си и имат приблизително еднаква продължителност на

живота.


Много изследвания потвърждават валидността на изброените твърдения

за голям брой съществуващи приложения. Нека разгледаме по-подробно

поколенията памет и това как те се използват за оптимизация на произво-

дителността на .NET garbage collector.



Поколение 0

Когато приложението се стартира, първоначално динамичната памет не

съдържа никакви обекти. Всички обекти, които се създават, стават част от

Поколение 0. Казано накратко Поколение 0 съдържа новосъздадените

обекти – тези, които никога не са били проверявани от garbage collector.

При инициализацията на CLR се определя праг за размера на Поколение

0.Да предположим, че приложението иска да създаде нов обект, F. Добавя-

нето на този обект би предизвикало препълване на Поколение 0. В този

момент трябва да започне събиране на отпадъци и се стартира garbage

collector.



Почистване на Поколение 0

Garbage collector процедира по описания по-горе алгоритъм и установява

че обекти B и D са отпадъци. Тези обекти се унищожават и оцелелите

обекти A, C и E се пренареждат в долната (или лява) част на managed

heap. Динамичната памет непосредствено след приключването на събира-

нето на отпадъци изглежда по следния начин:

Сега оцелелите при преминаването на garbage collector обекти стават част

от Поколение 1 (защото са оцелели при едно преминаване на garbage

collector). Новият обект F, както и всички други новосъздадени обекти ще

бъдат част от Поколение 0.

Нека сега предположим, че е минало още известно време, през което

приложението е създавало обекти в динамичната памет. Managed heap

сега изглежда по следния начин:

Добавянето на нов обект J, би предизвикало препълване на Поколение 0,

така че отново трябва да се стартира събирането на отпадъци. Когато

garbage collector се стартира, той трябва да реши кои обекти от паметта

да прегледа. Както Поколение 0, така и Поколение 1 има праг за своя

размер, който се определя от CLR при инициализацията. Този праг е по-

голям от този на Поколение 0. Да предположим че той е 2MB.

В случая Поколение 1 не е достигнало прага си, така че garbage collector

ще прегледа отново само обектите от Поколение 0. Това се диктува от

правилото, че по-старите обекти обикновено имат по-дълъг живот и сле-

дователно почистването на Поколение 1 не е вероятно да освободи много

памет, докато в Поколение 0 е твърде възможно много от обектите да са

отпадъци. И така, garbage collector почиства отново Поколение 0, оцеле-

лите обекти преминават в Поколение 1, а тези, които преди това са били в

Поколение 1, просто си остават там.

Забележете, че обект C, който междувременно е станал недостъпен и сле-

дователно подлежи на унищожение, в този случай остава в динамичната

памет, тъй като е част от Поколение 1 и не е проверен при това премина-

ване на garbage collector.

Следващата фигура показва състоянието на динамичната памет след това

почистване на Поколение 0.

Както вероятно се досещате, с течение на времето Поколение 1 бавно ще

расте. Идва момент, когато след поредното почистване на Поколение 0,

Поколение 1 достига своя праг от 2 MB. В този случай приложението

просто ще продължи да работи, тъй като Поколение 0 току-що е било

почистено и е празно. Новите обекти, както винаги, ще се добавят в

Поколение 0.




Сподели с приятели:
1   2   3   4




©obuch.info 2024
отнасят до администрацията

    Начална страница