Кратко съдържание



страница53/73
Дата21.07.2018
Размер9.03 Mb.
#76887
1   ...   49   50   51   52   53   54   55   56   ...   73

Съдържание


  • Масиви в .NET Framework

  • Многомерни масиви. Масиви от масиви

  • Типът System.Array

  • Сортиране на масиви и двоично търсене

  • Колекции в .NET Framework

  • IList, ArrayList, Queue и Stack

  • IDictionary и Hashtable. Собствени хеш-функции

  • Класът SortedList

В тази тема ...


В настоящата тема ще се спрем на масивите и колекциите в .NET Framework. Ще разгледаме видовете масиви: едномерни, многомерни и масиви от масиви (т. нар. назъбени масиви), както и базовия тип за всички масиви System.Array. Ще се запознаем с начините за сортиране на масиви и търсене в тях. Ще разгледаме с колекциите и тяхната реали­зация в .NET Framework: класовете ArrayList, Queue, Stack, Hashtable и SortedList, както и интерфейсите, които те имплементират.

Какво e масив?


Масивите са наредени последователности от еднакви по тип елементи. Те представляват механизми, който ни позволяват да третираме тези после­дователности като едно цяло.

Деклариране на масиви


Масиви в C# декларираме по следния начин:

int[] myArray;

В случая сме декларирали масив с име myArray от целочислен тип (System.Int32). В началото myArray има стойност null, тъй като не е заделена памет за елементите на масива.

Заделяне на масиви


Със следния код заделяме (алокираме) масив в C#:

myArray = new int[5];

В примера се заделя масив с размер 5 елемента от целочислен тип.

При заделяне на масив CLR автоматично инициализира всички негови елементи с неутрална стойност (0 или null). В нашия пример всеки от тези 5 елемента е със стойност 0, за разлика от други неуправлявани среди, където стойностите ще са произволни (C, C++). Адресът на блока памет, заделен за този масив се записва в променливата myArray.




Масивите в .NET Framework


Всички масиви в .NET Framework наследяват типа System.Array, което означава, че те винаги са референтни типове и се разполагат в блокове от динамичната памет (т. нар. managed heap).

От своят страна типът System.Array имплементира следните интерфейси: ICloneable, IList, IEnumerable и ICollection, които позволят масивите да се използват лесно в различни ситуации. Ще разгледаме тези интер­фейси малко по-късно в настоящата тема.

Тъй като са референтни типове масивите винаги се предават по референ­ция (т.е. по адрес, а не по стойност). Ако искаме да подадем даден масив като параметър, но да защитим от промяна стойностите на неговите елементи, трябва да подадем негово копие. Копие на масив можем да направим чрез статичния метод Array.Copy(…). Обърнете внимание, че този статичен метод прави плитки копия на елементите на масива.

Елементи на масивите


Достъпът до елементите на масивите е пряк, по индекс (пореден номер на елемента). Масивите обикновено са нулево-базирани, т.е. номерацията на елементите започва от 0. В .NET Framework обаче могат да се създадат и масиви с ненулева долна граница. Елементите на масивите са достъпни както за четене така и за писане.

Достъпът до елементите на масивите е проверен, т.е не се допуска изли­зане извън границите и размерностите на масив и при всеки достъп CLR проверява дали индексът е валиден и ако не е, се подава изключение System.IndexOutOfRangeException. Естествено тази проверка си има и своята цена и това е производителността. CLR обаче ни предоставя въз­можността да я изключим, като използваме ключовата дума unsafe. Тя се използва винаги, когато искаме да извършваме операции свързани с указатели. С unsafe трябва да обозначим метода, който ще извършва тези операции. Ето пример:



unsafe static void FastArrayAccess(int[] myArray)

{

// Acess the array elements here with no checks



}

При компилация на код, който използва unsafe трябва да укажем опцията на компилатора, че кодът има unsafe блокове. Ето как става това:

csc.exe /unsafe UnsafeArrayAccess.cs

Ако за нашето приложение бързодействието е от най-голямо значение можем да използваме unsafe код. Не трябва да забравяме обаче, че кодът, който пишем, вече няма да е управляван (managed) и CLR няма да се грижи за неговата обезопасеност.

Видове масиви


В .NET Framework се подържат едномерни, многомерни и масиви от масиви ("назъбени" масиви). Всеки от тези видове пази в себе си инфор­мация за броя на размерностите си (т. нар. ранг), както и грани­ците на всяка от тях. CLR е оптимизиран за работа с едномерни, нулево-базирани масиви, затова се препоръчва тяхното използване, когато е възможно. Масивите могат да се инициализират при деклари­ране.

Масиви – пример


Ще илюстрираме работата с масиви със следния пример:

int[] primes = {2, 3, 5, 7, 11, 13, 17, 19};

foreach (int p in primes)

{

Console.Write("{0} ", p);



}

Console.WriteLine();

// Output: 2 3 5 7 11 13 17 19
for (int i = 0; i < primes.Length; i++)

{

primes[i] = primes[i] * primes[i];



}
foreach (int p in primes)

{

Console.Write("{0} ",p);



}
Console.WriteLine();

// Output: 4 9 25 49 121 169 289 361



В горния пример първоначално създаваме едномерен масив от тип System.Int32, ини­циа­ли­зи­ра­ме го с първите 8 прости числа, след което го извеждаме в конзолата.

След това на всеки елемент му се присвоява за стойност неговия квадрат. Забележете, че се използва свойството Length, което връща броя на елементите на масива. В .NET Framework всеки масив знае своята дъл­жина.

Най-накрая новополучените стойности отново извеждаме на екрана чрез цикъл, реализиран с конструкцията foreach. Използването на foreach е възможно, защото масивите реализират интерфейса IEnumerable.

Прости числа – пример


В следващия пример ще използваме масиви за да реализираме един от най-старите алгоритми – решето на Ератостен:

using System;
class PrimeNumbersDemo

{

static void Main()



{

const int COUNT = 100;

bool[] prime = new bool[COUNT+1]; // array [0..100]

for (int i = 2; i <= COUNT; i++)

{

prime[i] = true;



}

for (int p = 2; p <= COUNT; p++)

{

if (prime[p])



{

Console.Write("{0} ", p);

for (int i = 2*p; i <= COUNT; i += p)

{

prime[i] = false;



}

}

}


Console.WriteLine();

}

}



Резултатът от изпълнението на програмата е следният:


Как работи примерът?


Алгоритъмът на Ератостен работи по следния начин: записваме числата от 2 до n (в нашия случай 100) в редица. Първоначално всички числа са незачертани. Намираме първото незачертано число – в началото това е 2, маркираме го като просто и зачертаваме всяко кратно на 2 число в редицата. Продъл­жаваме по същия начин със следващото незачертано число – 3. Процесът продължава докато не остане нито едно незачертано число. Тогава всички маркирани числа са прости.

Сега да се спрем по-подробно на конкретната реализация на C#. За маркирането и зачертаването на елементите ще използваме масив от тип System.Boolean. Понеже CLR инициализира по подразбиране булевите стойности с false, първият цикъл в кода им задава стойност true, като по този начин маркираме всички числа като прости. След това започваме цикъл по дължината на масива (COUNT) и за всяка негова итерация проверяваме дали текущото число е маркирано, ако е извеждаме го и зачертаваме неговите кратни. В крайна сметка ако елемент на масива prime има стойност true, неговият индекс е просто число.

Забележка: първият цикъл започва от 2, защото както знаем 1 не е просто число.

Масиви от референтни типове – пример


Със следващия пример ще илюстрираме използването на масив, чиито елементи са референтни типове, в частност – инстанции на класове дефинирани от нас.

using System;
class Animal

{

public virtual void Eat()



{

Console.WriteLine("Animals eat food");

}

}
class Tiger : Animal



{

public override void Eat()

{

Console.WriteLine("Tigers eat meat");



}

}
class Cow : Animal

{

public override void Eat()



{

Console.WriteLine("Cows eat grass");

}

}
class ArrayTest



{

static void Main()

{

Animal[] animals = new Animal[3];



animals[0] = new Animal();

animals[1] = new Tiger();

animals[2] = new Cow();
foreach (Animal animal in animals)

{

animal.Eat();



}

}

}



В примера се дефинира клас Animal, който има виртуален метод Eat(). Класът Animal се наследява от други два класа – Tiger и Cow, които от своя страна припокриват (override) този виртуален метод. В Main() метода създаваме масив от обекти от тип Animal. След създаването на масива animals, всеки един от неговите елементи е инициализиран със стойност null, защото Animal е референтен тип и се създават само нулеви референции към него, а действителните обекти се създават на следващи­те 3 реда. Обърнете внимание, че в масива можем да записваме инстан­ции не само към Animal, а и към всички класове, които са негови наследници. Накрая за всеки елемент на масива извикваме метода Eat(). Благодарение на полиморфизма резултатът от изпълнението на програ­мата е следният:





Сподели с приятели:
1   ...   49   50   51   52   53   54   55   56   ...   73




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

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