Clr, създаване и инсталиране на. Net компоненти, типове данни Стоян Йорданов Common Language Runtime (clr)



Дата30.07.2017
Размер200.17 Kb.
#26849
Програмиране за платформата .NET
CLR, създаване и инсталиране на .NET компоненти, типове данни

Стоян Йорданов
Common Language Runtime (CLR)
Какво е CLR? Защо е Managed Execution Environment?

  • Common Language Runtime е сърцето на .NET Framework.

  • Улеснява разработката на приложения, предоставяйки гъвкава и сигурна среда за изпълнение

  • Нарича се още managed environment, защото управлява изпълнението на кода, както и важни дейности като управление на сигурността (security) и паметта (memory management, garbage collection), поддръжка за дебъгери, както и взаимна работа на COM компоненти и managed code.

Assemblies and Metadata

  • Assembly-тата са основната единица за споделяне, инсталация, versioning и сигурност – аналогично на DLL.

  • Assembly-тата се състоят от изпълним код (MSIL), метаданни и ресурси, но не е задължително те да се намират физически в един файл – групирането в асембли може да бъде чисто логическо.

  • Assembly manifest – асемблито съдържа блок от данни, наречен “манифест”, изреждащ файловете, от които се състои асемблито, изискваните версии на ползвани други асемблита, секюрити информация.

  • Когато асемблито се състои от един файл, той съдържа манифеста. Когато е разпределено в няколко файла (модула), манифестът може да бъде отделен файл, или пък да се съдържа в някой от PE файловете, съставящи асемблито.

  • Метаданни (metadata) – информация (във вид на таблици), напълно описваща всичко, дефинирано в даден модул, ползваните типове данни, обръщения към други асемблита и т.н.

  • Метаданните дават на Common Language Runtime цялата информация, която му е необходима за намиране и зареждане на класове, управление на сигурността, намиране на извиквани методи, предоставяне на т.нар. reflection и т.н.

  • Метаданните са заместител на type library-тата, регистрирането в системното registry, и други технологии, използвани в миналото. Тъй като са вградени в EXE-то или DLL-а, за който се отнасят, метаданните е невъзможно да бъдат объркани, а освен това улесняват и инсталацията / деинсталацията на компоненти.

Intermediate Language

  • MSIL (Microsoft Intermediate Language), още наричан “managed code”, е набор от инструкции, към който компилаторите за CLR превеждат изходния код от по-висок език.

  • MSIL е стеково базиран, CPU-независим език. Въпреки, че е език от ниско ниво, все пак е на по-високо ниво от повечето асемблерни езици – има инструкции за създаване, инициализиране и извикване на методи (включително и виртуални) на обекти, контролни, аритметични и логически операции, както и обработка на изключения. Включени са и инструкции за директен достъп до паметта.

  • Преди да може да бъде изпълнен, MSIL трябва да се конвертира до код за специфичния процесор, на който е стартирана програмата – т.нар. “native code”. Това се извършва от JIT компилатор, написан за съответната софтуерна архитектура.

  • MSIL може да се конвертира до native още при инсталация на приложението, чрез програмата ngen.exe (native image generator), която се разпространява с .NET Framework. Използва се така: ngen.exe MyAssembly.exe.

  • Можем да инспектираме метаданните и MSIL кода в дадено асембли с програмата ildasm.exe – дизасемблерът на IL. Обратната му програма е ilasm.exe – IL асемблер.

Application Domains

  • При повечето операционни системи се използват границите на процесите (process boundaries), за да бъдат изолирани отделните процеси един от друг и да не могат да си навредят. Извикване на методи от друг процес става през т.нар. прокси, което е сравнително бавна операция.

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

  • Преминалият проверката код дава възможност да се постигне изолация, сравнима по сигурност с изолацията на процесите, но на много по-малка цена – това се постига от application domain-ите.

  • Application domain-ите са сигурен механизъм, който CLR използва за изолация. Няколко application domain-а могат да вървят в рамките на един процес на операционната система, като по този начин се постига по-висока ефективност, без да се жертва сигурност.


Създаване и инсталиране на .NET компоненти
Създаване на .NET компоненти и изпълними файлове

  • .NET асемблитата се генерират от компилатора за съответния език от високо ниво. За C# това е csc.exe, а за VB – vbc.exe – и двата компилатора идват с .NET Framework SDK.

  • На компилатора се указва дали да компилира до DLL или до EXE – csc.exe /target:library SomeAssembly.cs или /target:exe

  • Реферирани асемблита се указват с /reference: опция, разделени със запетайки

  • Асемблитата, към които се реферира, ще бъдат търсени от runtime-а в текущата директория, в Global Assembly Cache, или в други директории, ако са указани в config файла на приложението.

  • Възможно е да се компилира и до модул - /target:module, който след това да бъде включен в асембли с AL.exe – асембли линкера. Така, при изпълнението на програмата, се зареждат само модулите, които са необходими. Това обаче не може да се направи от Visual Studio.

Преносими изпълними файлове (Portable Executables)

  • Изпълнимите файлове при Windows – EXE и DLL – спазват специален файлов формат, наречен “Portable Executable” (PE).

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

  • Във формата PE е добавено специално поле, което указва, че даден файл съдържа .NET асембли. В CLR Data секцията се съдържат метаданните и MSIL кода на асемблито. Windows XP и Windows .NET поддържат CLR хедъра, и при изпълнение на такъв файл извикват .NET Framework.

  • За по-старите версии на Windows, които не разбират този хедър, входната точка на изпълнимия файл сочи към специален код, т.нар. stub code, като всичко, което той прави, е да зареди .NET Framework – mscoree.dll, и да му предаде управлението.

Private and Shared assemblies

  • Прости приложения се инсталират, като просто се копират на клиентския компютър, на който трябва да има инсталира .NET Framework. Не изискват писане в registry-то, и могат да бъдат деинсталирани чрез просто изтриване. По този начин се елиминира “DLL Hell”.

  • Компонентите за частна употреба на някакво приложение се наричат private assemblies; копират се в директорията на приложението.

  • Компонентите, които се споделят от няколко свързани приложения също са private assemblies; копират се в някаква обща директория

  • Компоненти, които се споделят с други, неизвестни приложения, се наричат “споделени асемблита” – shared assemblies. Те се инсталират в Global Assembly Cache (GAC) и трябва да притежават някои специални свойства, като например т.нар. “силно име” – strong name, което позволява да бъде определено еднозначно асемблито и версията му, която се изисква.

  • Силното име се състои от идентичността на асемблито, което включва името на асемблито, номера на версията, информацията за културата на приложението (използвана за интернационализация), и публичния ключ, свързан с асемблито – това е публичният ключ на издателя на асемблито.

  • Публичният ключ се използва за проверка на автентичността на асемблито, което е подписано със съответния частен ключ. Двойка публичен / частен ключ се генерират с програмата sn.exe: sn.exe /k SomeKey.snk.

  • Версията и ключовете се указват в кода на асемблито чрез атрибутите AssemblyVersion и AssemblyKeyFile:
    [assembly: System.Reflection.AssemblyVersion(“1.0.0.0”)]
    [assembly: System.Reflection.AssemblyKeyFile(“SomeKey.snk”)]

  • Инсталацията в Global Assembly Cache се извършва чрез инструмента gacutil.
    Инсталация: gacutil /i MyAssembly.dll.
    Преглед: gacutil /l
    Деинсталация: gacutil /u MyAssembly

Версии

  • Асемблитата си имат версии. Версията е във формат ...

  • Версия с променени major или minor компоненти се счита за несъвместима версия; промяна на build – може би е съвместима; сменен revision – съвместима версия. Тези неща могат да се укажат и в конфигурационния файл на съответното приложение.

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

  • Свързването с някакво специфично асембли по време на изпълнение на приложението може да бъде контролирано чрез application policy (SomeApplication.exe.config), publisher policy и administrator policy (Machine.config) – в този ред. Това става чрез специални XML конфигурационни файлове

  • Версиите на асемблита, които нямат силно име, не се проверяват.


Накратко за основната библиотека Framework Class Library (FCL) – I/O, Collections, Regular Expressions, Internet Classes, Data Access, Reflection
Типове данни
Основни типове данни, Common Type System (CTS)

  • Common Type System е общата система от типове, използвана при .NET. Тя позволява на програми, написани на какъвто и да е език за програмиране, да бъдат съвместими една с друга. Типовете данни, предоставяни от различните езици за програмиране, се свеждат до типове от CTS при компилирането на програмата (например типът int от C# се свежда до System.Int32 от CTS).

  • Всички типове от CTS са обекти, които могат да имат полета, конструктор / конструктори, методи, свойства

System.Object

  • Типът System.Object е основният тип данни в .NET Framework, и всички други типове наследяват него. System.Object предлага следните методи:

    • Equals – публичен метод, който определя дали даден обект е равностоен на текущия обект.

    • ReferenceEquals – публичен статичен метод, който определя дали два обекта са един и същи обект (в смисъл на instance).

    • GetHashCode – публичен метод, който служи като хеш-функция на даден тип данни. Може да се използва в хеширащи алгоритми и структури от данни.

    • GetType – публичен метод, връщащ типа на обекта.

    • ToString – публичен метод, връщащ стринговото представяне на обекта.

    • Finalize – protected метод, позволяващ на обекта да освободи заеманите ресурси, преди изчистването му от Garbage Collector-а.

    • MemberwiseClone – protected метод, създаващ “плитко” копие на текущия обект (т.е. копиращ дословно член-променливите му).

  • Интерфейсите са единствените типове, които не наследяват от System.Object. Те нямат имплементация, а представляват просто прототип, или описание, на методи, събития и свойства. Предоставят механизъм за множествено наследяване. Трябва да бъдат имплементирани напълно от класовете, които ги наследяват.

Value types vs. reference types

  • Value типовете наследяват от System.ValueType. Това са прости типове, които се заделят от стека, а не от т.нар. “heap”.

  • System.ValueType наследява от System.Object, така че всички стойностни типове се държат като останалите обекти.

  • Дефинирани са прости типове, като Byte, Int16, Int32, Int64, Boolean, Char, Single, Double, Decimal, IntPtr и String. Освен това са достъпни и други вградени типове.

  • Когато се предават като аргументи на методи, стойностните типове се предават по стойност, освен ако изрично не се укаже предаване по reference чрез ключовата дума ref.

  • Нови value типове могат да бъдат дефинирани чрез ключовата дума struct. Всички стойностни типове са sealed, т.е. не могат да бъдат наследявани.

  • Reference типовете са типове, наследяващи от System.Object, без да наследяват System.ValueType. Заделят се на heap-а чрез ключовата дума new, освобождават се от Garbage Collector-а, и могат да бъдат разглеждани като указатели в C++, макар че има разлики.

  • Когато се предават като аргументи, на стека се поставя стойността на reference-а към обекта. Така методът може да работи с оригиналния обект, към който reference-ът сочи.

  • И двата вида типове могат да предоставят член-променливи, свойства (properties) и методи.

Изброими типове

  • Изброимите типове при .NET наследяват от класа System.Enum, който наследява от System.ValueType.

  • enum SomeEnum : ulong { Blah0, Blah1, Blah2 };

  • Базов тип може да е всеки примитивен тип освен System.Char. Номерацията започва от 0, освен ако изрично не се укаже друго.

  • GetHashCode връща истинската стойност, която стои зад enum-а. ToString връща името на съответната константа във въд на стринг, а чрез Parse стрингови представяния (например прочетени от клавиатурата) могат да бъдат конвертирани обратно към enum (System.Enum.Parse(typeof(SomeEnum), “Blah0”)).

  • Битови флагове могат да се създават чрез атрибута [Flags]. Тогава трябва ръчно да се специфицират стойностите на константите.

Характеристики на обектите в .NET Framework

  • Абстракция – основна характеристика на обектно-ориентираното програмиране; позволява да погледнем на обекта като едно цяло, да го обобщим, с което улесняваме решаването на конкретен проблем.

  • Енкапсулация – скривне на детайлите по имплементацията на някакъв клас. Модификаторите, които могат да се използват за членове на класове при C#, са: public, protected, internal (достъп имат само класове от същото асембли), private. Подразбиращото се при enums и интерфейси е public, а при структури и класове – private. Могат да се използват и пред типове. В C# не са позволени комбинации, освен protected internal.

  • Наследяване – метод за повторно използване на код. Един клас може да наследи (използва имплементацията) на друг клас. Например при CTS всички класове наследяват System.Object, т.е. притежават неговите методи, свойства и член-променливи. При .NET се поддържа само единично наследяване, освен когато става дума за интерфейси – те са единственият механизъм за множествено наследяване.

  • При наследяване, наследяващия клас може да “скрива” методи на наследявания. Това става с ключовата дума new така: public new void MyMethod(). Тя не е задължителна, но подобрява четимостта. Ако я няма, се появява warning.

  • Ако някой клас има абстрактни методи, полета или свойства (abstract), т.е. не предоставя имплементация, наследяващите го класове трябва да ги дефинират чрез ключовата дума override.

  • Запечатани класове (маркирани с ключовата дума sealed) не могат да бъдат наследявани.

  • Полиморфизъм – постига се с ключовите думи virtual / override.

  • Запечатани методи – повече не могат да бъдат предефинирани. Например public sealed override void SomeMethod();

Работа с типове

System.Object

  • Хеш кодове

    • Хеш функциите се използват, за да съпоставят бързо число на някакъв обект. Това число след това може да бъде използвано за бързо търсене в таблици и колекции.

    • Класът System.Object предоставя метод GetHashCode, който връща стойност от тип int. Може да се наследява в класовете и структурите, създавани от програмистите, например за да връщат една и съща хеш стойност, когато два обекта са равностойни.

    • Не бива да се хвърля exception от GetHashCode, защото може да бъде извиквана често и се очаква да работи стабилно.

  • Идентичност

    • Идентичността позволява да се провери дали два обекта са всъщност един и същи обект.

    • При .NET това се постига с метода ReferenceEquals на – статичен за System.Object. Това позволява да се провери дали става дума за същия обект – все едно да сравним адресите на обектите при C++, или адресите на IUknown при COM.

  • Равностойност

    • Два обекта могат да бъдат проверени за равностойност чрез метода Equals и операторите за сравнение == и !=.

    • Подразбиращата се имплементация на Equals извиква ReferenceEquals, но може да се предефинира, за да извършва истинско сравнение. В такъв случай трябва да се предостави имплементация и на == и !=.

    • Винаги когато се предефинира Equals, трябва да се предефинира и GetHashCode, за да може два равностойни обекта да имат еднакъв хеш код.

    • Equals трябва да се предефинира, когато са предефинирани операторите == и !=, или се наследява IComparable.

    • Методите за сравнение не бива да хвърлят exception-и.

  • Представяне като стринг

    • Всички обекти могат да бъдат конвертирани до стринг чрез метода ToString.

    • Подразбиращото се поведение на ToString е да върне името на класа. Затова е хубаво да се предефинира, за да връща нещо по-смислено.

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

  • Статични конструктори (клас конструктори)

    • Конструкторите, дефинирани като статични, се използват, за да се инициализират статичните полета на даден клас.

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

    • Няма public или private модификатори

    • При изрично инициализиране на статичните променливи (например static int someMember = 0), автоматично се създава статичен конструктор. Първо се изпълнява кода за инициализацията, а след това кода от ръчно написания статичен конструктор (ако има такъв).

  • Частни (private) конструктори

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

    • Това позволява реализирането на специално поведение, като например т.нар. singleton класове (класове, които могат да имат само една инстанция), или пък класове, които имат само статични методи и не трябва да бъдат инстанциирани.

    • В случай, че класът трябва да може да бъде инстанцииран (например singleton клас), това може да бъде постигнато чрез някакъв статичен метод или свойство на класа, който може да прави и допълнителни проверки или да реализира специално поведение. Тъй като статичния метод принадлежи на класа, той има достъп до частния конструктор

Конвертиране на типовете

  • Конвертирането на типовете представлява превръщането на стойността на променлива от един тип към стойност от друг тип.

  • Конвертирането бива изрично (explicit) и подразбиращо се (implicit).

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

  • Конвертирания, които “разширяват” типовете, например от byte към int, от int към long и т.н. могат да бъдат по подразбиране.

  • Могат да бъдат създавани дефинирани от потребителя конвертирания, чрез дефиниране на оператори за конвертиране на типовете. Синтаксисът е следният:
    public static [implicit | explicit] operator Dest-Type-Name( Src-Type-Name operand)

  • Конвертиранията по подразбиране не трябва да хвърлят изключения.

  • Ако може да стане загуба на данни, или може да бъде генерирано изключение, конвертирането трябва да бъде изрично.

Casting

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

  • Често се използва за смяна на типа на референцията към даден обект към базов клас или интерфейс (нагоре по йерархията) или към наследен клас (надолу).

  • Когато се cast-ва нагоре към базов клас, може да става по подразбиране.

  • При невъзможно конвертиране се генерира InvalidCastException.

Оператори за работа с типове в C#

  • Операторът is се използва за проверка на типа на обект. Ако обектът е от указания тип, се връща true, иначе – false.

  • Операторът as се използва за опит за конвертиране на обект към даден тип. Този оператор не генерира изключение – ако конвертирането е невъзможно, връща стойност null.

  • Операторът typeof се използва при Reflection за по-сложни дейности. Той връща обект от тип System.Type. Може да бъде използван и методът GetType.

Boxing и unboxing

  • Boxing е конвертиране, което се извършва автоматично, когато даден value тип трябва да бъде конвертиран към обект (за да му бъде извикан някой метод, или въобще да може да бъде третиран като System.Object). Това важи само за System.Object – всички останали типове трябва да имат съответните оператори за конвертиране.

  • При операцията boxing се заделя място на heap-а за новия обект, където се копира стойността от стека.

  • За да може box-нат стойностен тип да бъде третиран отново като value type, трябва да бъде извършен обратния процес – unboxing. При него стойността на обекта от heap-а се копира на стека, където се ползва unbox-натия обект.

Низове, масиви и колекции

Низове

  • В C#, string е псевдоним за System.String.

  • Низовете, веднъж създадени, не могат да бъдат променяни. Вместо това, трябва да бъде създаден нов низ, което може да е бавна операция. За бързи промени на низове, трябва да бъде използван класът System.Text.StringBuilder.

  • Голяма част от системните типове, и в частност числовите типове (като например System.Boolean, System.Int32 и т.н.) предоставят метода Parse, който позволява някакъв низ да бъде конвертиран до съответния тип. Parse приема до три параметъра – низът, който трябва да бъде конвертиран, изброима стойност от тип System.Globalization.NumberStyles, и обект от тип NumberFormatInfo. Параметърът от тип NumberStyles се използва за конвертиране на низове, които имат и нецифрови символи. Например числото 1,234 трябва да бъде парснато така:
    int.Parse(“1,234”, NumberStyles.AllowThousands)

  • Форматиращи низове – възможност, предоставена от .NET Framework, която позволява низове да бъдат форматирани по специален начин, зададен от програмиста. Използват се с методи, конвертиращи .NET типове към низ, а освен това и при Console.WriteLine, String.Format, както и при други, като например ToString методите, предоставяни от много типове. Стандартните форматиращи низове имат формата Xn, където X е форматиращ спецификатор, а n – спецификатор на точността. Например “c” – представяне като валута ($345.00), а “c3” – валута с точност 3 знака ($345.000). За дати – “D” – дата в дълъг формат, “d” – в кратък.

  • Смяна на case-а: чрез методите String.ToUpper и String.ToLower.

  • Сравнение – String.Compare сравнява два низа и връща число. String.StartsWith проверява дали даден низ започва с някакъв подниз. String.IndexOf връща индекса на първото срещане на символ или подниз.

  • Trim премахва whitespace-а от двете страни на низа. PadLeft и PadRight допълват низ отляво или отдясно с интервали. String.Remove премахва определен брой символи от зададена позиция.

  • Низове могат да бъдат разделяни на поднизове чрез метода Split, приемащ разделител. Ако той е null, се разбива по whitespace-а. Join – обратно – слива няколко низа в един, като ако разделителят е пропуснат, използва интервал.

  • При C# са предефинирани операторите == и !=, които сравняват низовете, а не референциите към тях. Операторът + може да се използва за конкатенация на низове, а [] – за индексиране на отделни символи в низа. Цитиране с @ кара компилатора да не обработва escape последователностите (например @”C:\MyDir”).

StringBuilder

  • StringBuilder се използва за лесно конструиране на низове, спестявайки операциите по непрестанно създаване на нови низове.

  • StringBuilder заделя нова памет след края на низа, ако това е необходимо при добавяне на нови символи отзад. Може да се специфицира начален размер на буфера – т.нар. капацитет, с което могат да се спестят излишни заделяния, ако предварително знаем какъв размер на низа да очакваме.

  • StringBuilder предлага методите Append, AppendFormat, Insert, Remove, Replace.

Масиви

  • Класът System.Array е базовият клас на всички масиви. Предлага методи за създаване, претърсване и сортиране на масиви.

  • Масивите могат да бъдат едномерни, многомерни, както и масиви от масиви (т.нар. “jagged arrays”).

  • System.Array наследява интерфейсите ICloneable, който позволява “клониране” на масиви, IList, който позволява отделно индексиране на елементите на масива, ICollection, който дефинира размерите и синхронизационните методи на всички видове колекции (от него идват property-тата Count – брой елементи в колекцията, IsSynchronized, което показва дали достъпът до колекцията е thread-safe, и SyncRoot, което връща синхронизационния обект на колекцията), и IEnumerable, който позволява лесно итериране (например чрез оператора foreach).

  • Чрез CreateInstance може да се създава инстанция на масив, със зададени измерения и границите им. Предлагат се статичните методи BinarySearch и Sort, които позволяват лесно претърсване и сортиране на едномерни масиви. Освен това се предлагат и свойствата IsFixedSize (винаги true за масиви) и IsReadOnly (винаги false за масиви), които са наследени от IList (има ги в IList и IDictionary), Length (връща общия брой елементи в масива) и Rank (връща размерността на масива), както и методите GetLength, GetLowerBound и GetUpperBound (опериращи с определено измерение на масива), и методите GetValue и SetValue.

  • Възможно е да има и празни масиви, т.е. с 0 елемента. Методи и свойства, връщащи масиви, е по-добре да връщат такива, отколкото null.

  • При C# масивите започват от 0. Размерът се задава при оператора new, а не при типа. Може да се инизиализират при декларация чрез { }.

  • Итерирането по масиви може да става чрез методите на техния enumerator, който се връща от метода GetEnumerator от интерфейса IEnumerable – MoveNext и Reset. Current свойството на enumerator-а връща System.Object – текущия елемент. Алтернативно може да става и чрез оператора foreach (който изисква масивът или колекцията, които се итерират, да имплементират IEnumerable).

  • За сортиране и претърсване, елементите на масива трябва да наследяват IComparable, или пък на методите да бъде предоставен IComparer обект. IComparable предоставя метод CompareTo, а IComparer – Compare.

Колекции

  • Колекциите, предоставени от .NET Framework, се намират в namespace-а System.Collections. Такива колекции са например ArrayList, който представлява динамично разрастващ се списък, BitArray, който представлява масив от булеви стойности, компактно пакетирани по една в бит, CollectionBase, ReadOnlyCollectionBase и DictionaryBase, които са базови класове за силно типизирани колекции, Hashtable, което е колекция, организирана по хеш кода на ключа, Queue – опашка, SortedList – сортиран списък, Stack – стек и др.

  • Списъците (например Array, ArrayList, StringCollection и т.н.) имплементират интерфейса IList. Той предлага свойствата IsFixedSize, IsReadOnly, Item, което в C# представлява индексатора на колекцията, както и методите Add, Clear, Contains, IndexOf, Insert, Remove и RemoveAt.

  • Речниците (като Hashtable, SortedList и т.н.) имплементират инрерфейса IDictionary, който позволява да бъдат изброени двойките ключ / стойност. Тези имплементации могат да бъдат само за четене или пък да позволяват писане, както и да бъдат с фиксиран или динамичен размер. IDictionary предлага свойствата IsReadOnly, IsFixedSize, Item – индексатора на колекцията в C#, Keys, което връща колекция (ICollection) с ключовете, и Values, което връща колекция със стойностите. Освен това се предлагат и методите Add, Contains, GetEnumerator и Remove.

  • SortedList поддържа два масива – един за ключовете, и един за стойностите. Предлага свойството Count, връщащо броя на елементите в списъка, и може да бъде индексиран в C# чрез индексатора си (или чрез Items свойството в други езици).

Делегати и събития

Делегати

  • Делегатите (System.Delegate) са обектно ориентиран, type-safe и сигурен механизъм за предаване на адресите на функции. Може да се каже, че са обектно-ориентираният еквивалент на указателите към функции, но без да имат техните недостатъци.

  • Делегат се декларира чрез ключовата дума delegate, следвана от сигнатурата на функцията, към която делегатът може да сочи. Например:
    delegate void SomeDelegate(int i)
    представлява делегат към void функция, приемаща int параметър.

  • При компилация, компилаторът скрито създава нов тип за декларирания от програмиста делегат. Този нов тип има конструктор и Invoke метод, които са силно типизирани.

  • За да бъде използван делегат, трябва да бъде създаден негов обект – чрез ключовата дума new. В конструктора се подава функцията, към която той трябва да сочи. Например:
    SomeDelegate d;
    d = new SomeDelegate(this.MyFunction);


  • Делегатът се извиква чрез името си, следван от параметрите, които трябва да бъдат предадени на извикваната функция.

  • Когато трябва да се извикат няколко метода едновременно (например за да бъдат уведомени за нещо), може да бъде използван multicast delegate (System.MulticastDelegate), който наследява System.Delegate.

  • Multicast делегатите могат да имат повече от една функция в своя invocation list. При извикване, извикват функциите последователно.

  • Multicast делегатите предлагат функциите Combine и Remove. Combine приема два параметъра от тип делегат, и връща делегат, съдържащ обединението на invocation списъците на подадените два делегата (последователно). Remove премахва последното от срещанията на всеки от елементите на втория подаден делегат от списъка на първия. Методът GetInvocationList може да бъде използван за получаване на invocation списъка на даден делегат.

  • При C#, делегатите, които връщат void, се реализират чрез multicast делегати. Могат да бъдат използвани операторите + и – (и съответно += и -=) вместо извиквания към Combine и Remove, което прави програмата по-четима. При компилация тези оператори се заместват със съответните извиквания.

Събития

  • Събитията са обектно ориентиран, type-safe и сигурен механизъм за уведомяване на даден клас, че нещо, от което той се интересува, се е случило. Събитията са важен механизъм, широко използван в системите с потребителски интерфейс, но имат и много други приложения.

  • За да се декларира дадено събитие, първо трябва да се декларира делегат за събитието. След това се декларира и самото събитие чрез ключовата дума event:
    public static event SomeDelegate SomeEvent;

  • Когато се декларира събитие, компилаторът създава частно поле в класа, което се обръща към invocation списъка на някакъв делегат. Освен това, компилаторът създава методите add_ИмеНаСъбитието и remove_ИмеНаСъбитието, чрез които се добавят или премахват функции, които да бъдат уведомявани за събитието. Тези функции са достъпни в C# чрез операторите += и -=, което прави програмата по-четима.

  • Клиентите на събитието имат достъп единствено до тези две функции, което повишава сигурността.

  • Абонирането за събитие става, като се създаде делегат, инициализиран с функцията, която ще обработва събитието, и той се добави към invocation списъка на събитието:
    SomeClass.SomeEvent += new SomeDelegate(SomeHandler);

  • Събитие се предизвиква (raise), като първо се провери дали събитието съществува (т.е. дали не е null), след което се извиква чрез делегата си:
    if(SomeEvent != null) SomeEvent();

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

  • Препоръки за използване на събития:

    • Имената на събитията е хубаво да се състоят от глагол.

    • Трябва да се използва терминът “raise”, а не “fire”

    • Аргументите на събитията трябва да наследяват System.EventArgs, като има два аргумента: object sender, EventArgs e

    • За предизвикване на събития е добре да се ползва protected virtual метод.

Делегати се ползват, когато имаме нужда от указател към функция, или callback. Събития се ползват, ако клиентът трябва да се абонира за тях. Интерфейси се ползват, ако трябва да се реализира по-сложно поведение (като викане последователно на няколко функции и т.н).
Каталог: dotnet -> 2003 -> lectures
lectures -> Програмиране за платформата. Net взаимодействие между managed и unmanaged код Стоян Йорданов
lectures -> Потоци и файлове Георги Иванов
lectures -> Програмиране за платформата. Net работа с xml стоян Йорданов Какво е xml?
lectures -> Решение на проблема оператора delete
lectures -> Програмиране за платформата. Net web Services Стоян Йорданов Преглед
lectures -> Serialization
lectures -> Решение на проблема оператора delete
lectures -> Програмиране за платформата. Net работа с xml стоян Йорданов Какво е xml?


Сподели с приятели:




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

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