C# изборна дисциплина


Управление на достъпа до членовете на класа



страница5/5
Дата27.09.2016
Размер0.87 Mb.
#10960
1   2   3   4   5
15. Управление на достъпа до членовете на класа
Класът изпълнява две основни функции, осигуряващи инкапсулацията на данни. Първо, той свързва данните с кода, който ги обработва и, второ, класът осигурява средства за управление на достъпа до членовете на класа, които сега ще разгледаме. В С# достъпът до членовете на класа се осъществява малко по-сложно, отколкото някои от другите езици за програмиране. Основно се използват 2 базови типа членове на класа: public И private. Достъпът до член на класа обявен като public, може да се осъществява от всеки друг код на програмата, включително методи, определени вътре в други класове. В разгледаните досега примери, всички членове на класа бяха public. Достъпът до член на класа, обявен като private, може да се осъществява само от другите методи на същия клас. Кодът, определен извън границите на класа може да получи достъп до закрит член на класа само като използва открит метод на същия клас. Ограничаването на достъпа до членове на класа е основен принцип на ООП, тъй като това ограничаване защитава обектите от неправилно използване. Разрешаването на достъп до закритите членове на класа само чрез строго определен набор от методи, предотвратява присвояването на данните на некоректни стойности ( :^) ). Кодът, определен извън границите на класа не може непосредствено да присвоява стойност на скрит член на класа. Начинът и времето на използване достъпа до обекта също може строго да се контролира. Следователно, ако класът р реализиран правилно, то той създава черна кутия, която може да се използва, но вътрешното функциониране е защитено от несанкционирана намеса. В С# управлението на достъпа до членовете на класа се осъществява чрез 4 модификатора public, private, protected и internal.

Вече разгледахме модификаторите public I private. Protected се използва само при наследяване. Член на класа с модификатор internal е известен на цялата програма, но никъде повече. Ако член на класа няма модификатор, то той автоматично става private. Спецификацията на члена на класа, модификатора се задава първи. Да разгледаме един пример:

using System;
class MyClass

{

private int alfa;



int beta;

public int gamma;

public void setAlfa(int a)

{

alfa = a;



}

public int getAlfa()

{

return alfa;



}

public void setBeta(int a)

{

beta = a;



}

public int getBera()

{

return beta;



}

};

class AccessDemo



{

public static void Main()

{

MyClass ob = new MyClass();



ob.setAlfa(1);

ob.setBeta(2);

ob.gamma = 3;

Console.WriteLine(ob.getAlfa());

Console.WriteLine(ob.getBera());

//ob.alfa = 4; //error

Console.WriteLine(ob.gamma);

}

};



Ще разгледаме пример, в който се създава масив от тип int. В него е реализирана защита от обръщения към несъществуващи елементи на масива, т.е. обработват се ситуациите, когато се оказва индекс извън зададения диапазон. Алгоритъмит за такава обработка се нарича „алгоритъм за предотвратяване на сбоеве”, тъй като той предотвратява възникване на грешка при изпълнение на програмата, при която системата генерира съответно съобщение и изпълнението на програмата се прекратява. Реализацията на този алгоритъм се осъществява чрез инкапсулация на масива като закрит член на класа, което позволява да се предотврати всеки опит за достъп до елемент на масива с грешен индекс.

using System;


class FailSoftArray

{

private int[] a;



private int errval;

public int Length;

public FailSoftArray(int size, int errv)

{

a = new int[size];



errval = errv;

Length = size;

}

public bool ok(int index)



{

if (index >= 0 & index < Length)

return true;

return false;

}

public int get(int index)



{

if (ok(index))

return a[index];

return errval;

}

public bool put(int index, int val)



{

if (ok(index))

{

a[index] = val;



return true;

}

return false;



}

};

class FSDemo



{

public static void Main()

{

FailSoftArray fs = new FailSoftArray(5, -1);



//obqsneniq za parametrite na konstruktora

//i koj za kakvo e; obqsneniq i za tova, kakvo pravi samata programa

//dostyp do elementite (s get i put)

int x;


Console.WriteLine("Използване на алгоритъм за предотвратяване на сбоеве");

for (int i = 0; i < fs.Length * 2; i++)

{

fs.put(i, i * 10);



}

Console.WriteLine("Стойности на елементите на масива:");

for (int i = 0; i < fs.Length * 2; i++)

{

x = fs.get(i);



if (x != -1)

Console.Write(x + " ");

}

Console.WriteLine();



}

};
В С# има специален тип за член на клас индексатор, който позволява да се индексира обект на класа така, както масив. Освен това има по-съвършен начин за създаване на променливата Length и работа с нея чрез превръщането й в свойство. Модификаторът private може да се използва за усъвършенстване на класа Queue. Сега масивът от символи q, с който се моделира опашката, е закрит. Това не позволява от кода на програмата, използваща класа queue, да има обръщение към елементите на масива извън опашката. Индексите putloc и geloc, определящи началото и края на опашката също са закрити. Това гарантира осигуряване на правилен достъп до елементите на опашката.

class Queue

{

char[] q;



int putloc, getloc;

public Queue(int size)

{

q=new char[size+1];



putloc=getloc=0;

}

public void put(char ch)



{

if (putloc == q.Length - 1)

{

Console.WriteLine("\nОпашката е пълна");



return;

}

putloc++;



q[putloc] = ch;

}

public char get()



{

if (getloc == putloc)

{

Console.WriteLine("\nОпашката е празна!");



return (char)0;

}

getloc++;



return q[getloc];

}

};




16. Предаване на обекти на метод и връщане на обекти
В разгледаните досега програми параметрите на методите бяха от обикновен тип, като int и double, например. Но в метод могат да се предават и обекти. Да разгледаме проста програма, в която се сравняват призми, като стойностите на техните размери се съхраняват в обекти от клас Block:

using System;


class Block

{

int a, b, c;



int volume;

public Block(int i, int j, int k)

{

a = i;


b = j;

c = k;


volume = a * b * c;

}

public bool sameBlock(Block ob) //+obqsneniq kakvo pravi



{

if ((ob.a == a) && (ob.b == b) && (ob.c == c))

return true;

return false;

}

public bool sameVolume(Block ob) //+obqsneniq kakvo pravi



{

if (ob.volume == volume)

return true;

return false;

}
}

class PassOb

{

public static void Main()



{

Block o1=new Block(10, 2, 5);

Block o2=new Block(10, 2, 5);

Block o3 = new Block(4, 5, 5);

Console.WriteLine(o1.sameBlock(o2));

Console.WriteLine(o1.sameVolume(o2));

Console.WriteLine(o1.sameBlock(o3));

Console.WriteLine(o1.sameVolume(o3));

}

}

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



Има два начина за предаване на аргументи в подпрограма. Първият начин се нарича „извикване” или „заместване по стойност”. При него, формалният параметър на подпрограмата се замества или получава копие на стойността на аргумента. Следователно, изменение на параметъра на подпрограмата не указва влияние на аргумента, който се използва при нейното извикване. Вторият начин за предаване на аргументи се нарича „извикване” или „заместване по име”. В този случай, параметъра се замества с името на аргумента, т.е. с адреса на аргумента. Вътре в подпрограмата, този адрес се използва за достъп до аргумента, който е бил зададен при извикване на метода. Това означава, че изменението на параметъра ще влияе на аргумента, използван при извикване на подпрограмата. В С# се използват и двата начина за предаване на аргументи. При предаване в метода на стойности от обикновени типове, като Int или double, се използва начина за предаване по стойност. Да разгледаме един пример:

using System;


class Test

{

public void NoChange(int i, int j) //изпълнението на този метод няма



{ //да доведе до промяна на аргументите

i = i + j;

j = -j;

}

}



class CallByValue

{

public static void Main()



{

Test ob = new Test();

int a = 5, b = 20;

ob.NoChange(a, b);

Console.Write(a);

Console.WriteLine(b);

}

}

При предаване на обект, фактически се предава променлива от указателен тип, която сочи към обекта по стойност. Следователно и аргумента, и параметъра ще сочат към един и същ обект. В действителност, това означава, че обектът се предава на метода чрез извикване/заместване/ по име. Изменението на обекта вътре в метода води до изменение на обекта, използван като аргумент. Пример:



using System;
class Test

{

public int a, b;



public Test(int i, int j)

{

a = i;



b = j;

}

public void change(Test ob) //променя стойностите на член-применлива а и бю



{ //в обекта, използван като аргумент при извикване на аргумента

ob.a = ob.a + ob.b;

ob.b = -ob.b;

}

}



class CallByRef

{

public static void Main()



{

Test ob = new Test(15, 20);

ob.change(ob);

Console.Write(ob.a);

Console.WriteLine(ob.b);

}

}



В С# използването на модификатора на параметъра ref води до извикване, т.е. заместване по име вместо извикване по стойност. Модификаторът ref се задава както при дефиниране на метода, така и при неговото извикване. Той позволява на метода да изменя съдържимото на своите аргументи да разгледаме програма, в която се създава метода sqr, който връща квадрата на своя аргумент.

using System;

class refTest

{

public void sqr(ref int i)



{

i = i * i;

}

}

class RefDemo



{

public static void Main()

{

refTest ob = new refTest();



int a = 10;

ob.sqr(ref a);


Console.WriteLine(a);

}

}



Сега ще разгледаме пример, който разменя стойностите на два цели аргумента, придадени при извикване:

using System;

class Swap

{

public void swap(ref int i, ref int j)



{

int t;


t = i;

i = j;


j = t;

}

}



class SwapDemo

{

public static void Main()



{

Swap ob = new Swap();

int x = 10, y = 20;

ob.swap(ref x, ref y);

Console.WriteLine(x);

Console.WriteLine(y);

}

}

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



Модификаторът out също задава заместване по име, но той не се използва за предаване на информация вътре в метода, а за връщате на информация навън от него. При това, не е необходимо, преди извикване на метода, да се задава начална стойност на аргумента. Нещо повече, вътре в метода, параметър с модификатор out винаги се разглежда като нямащ начална стойност. Методът е длъжен да присвои стойност на параметъра, преди да завърши изпълнението. Ще разгледаме пример, в който се използва метод, връщащ два резултата:

using System;


class Rectange

{

int side1, side2;



public Rectange(int i, int j)

{

side2 = j;



side1 = i;

}

public int rectangeInfo(out bool isSqure)



{

if (side1 == side2)

isSqure = true;

else


isSqure = false;

return side1*side2;

}

};

class rectDemo



{

public static void Main()

{

Rectange ob = new Rectange(10, 23);



bool isSqr;

int area;

area = ob.rectangeInfo(out isSqr);

Console.WriteLine(isSqr);

Console.WriteLine(area);

}

};



При използване на модификатори ref и out за параметри от указателен тип, самият показател се предава по име. Това позволява на метода да промени стойността на указателя, който след изпълнението на методът, вече ще сочи към друг обект. Да разгледаме следната програма:

using System;


class Test

{

public int a;



public Test(int i)

{

a = i;



}

public void NoChange(Test obj)

{

Test newOb = new Test(0);



obj = newOb; //изпълнението на този оператор не влияе на аргумента;

}

public void change(ref Test obj)



{

Test newOb = new Test(0);

obj = newOb; //тук вече се променя obj

}

}



class testRef

{

public static void Main()



{

Test ob = new Test(100);

ob.NoChange(ob);

Console.WriteLine(ob.a);

ob.change(ref ob);

Console.WriteLine(ob.a);

}

}

Понякога се налага да се използват методи, на които се предават променлив или произволен брой аргументи. За създаване на параметър, който приема променлив брой елементи, се използва модификатор params. Модификаторът params се използва за обявяване на параметър като масив, който може да приема 0 и повече аргументи. Броят на елементите в масива ще бъде равен на броя аргументи, които се предават на метода, следователно, за да получи аргументите, програмата трябва да се обръща към масив. Като пример, да разгледаме метода minValue, който връща минималната стойност от няколко стойности, предадени му като аргументи:



using System;

class Min

{

public int minVal(params int[] nums)



{

int m;


if (nums.Length == 0)

{

Console.WriteLine("Грешка! За метода не са зададени аргументи");



return 0;

}

m=nums[0];



for (int i = 1; i < nums.Length; i++)

{

if (nums[i] < m)



m = nums[i];

}

return m;



}

}
class ParamsDemo

{

public static void Main()



{

Min ob = new Min();

int min;

int a = 10, b = 20;

min = ob.minVal(a, b);

min = ob.minVal(a, b, -1);

min = ob.minVal(a, b, -1, 0, 100);

//може да бъде подаден и масив от int:

int[] args={1,4, 12, 4, 26,2, 1, 345, 334};

min=ob.minVal(args);

}

}

На параметър с модификатор params може да се предаде произволен брой аргументи, но всички те трябва да бъдат съвместими с типа на масива, определен като параметър. Например:



min = ob.minVal(1, 2.2);

е грешно, тъй като стойност от тип double не се преобразува автоматино към тип int, какъвто тип има масива nums в minVal. При използване на параметър с модификатор params е необходимо да се контролира броя на елементите на масива, тъй като той може да приема произволен брой аргументи, включително и 0. например операторът:

min = ob.minVal();

е синтактически правилен, затова в метода minVal, преди обръщението към първия елемент на масива се проверява дали има в масива поне един елемент. Ако такава проверка няма, ще възникне изключителна ситуация, тъй като има грешка при изпълнение на програмата. Мотодър може да има обикновени параметри и не повече от един параметър с променлива дължина, който трябва да бъде последен от списъка с параметри. Да разгледаме един пример:

using System;

class MyClass

{

public void showArgs(string msg, params int[] nums)



{

foreach (int i in nums)

{

Console.WriteLine(i + " ");



}

Console.WriteLine();

}

}

class Demo



{

public static void Main()

{

MyClass ob = new MyClass();



ob.showArgs("В този метод, освен низа, има още 5 аргумента:", 1, 2, 3, 4, 5);

}

}



Методът може да връща данни от произволен тип, включително и обекти от някакъв клас. Да съставим програма, която извежда различни съобщения за грешка:

using System;

class ErrMsg

{

string[] msg ={"Грешка при извеждане.", "Грешка при въвеждане.",



"Недостатъчно свободно място на диска.",

"Излизане извън диапазона на индексите в масива"};

public string getErrMsg(int i)

{

if (i >= 0 && i < msg.Length)



return msg[i];

return "Въведен е непрaвилен код за грешка";

}

};

//в зависимост от кода на грешката, получен като аргумент,



//методър getErrMsg връща съответния елемент на масива, който е

//обект от тип sting. По същия начин могат да се връщат обекти

//от класове, създадени от потребителите
Да разгледаме модифицирана горната програма:

using System;


class Err

{

public string msg;



public int severity;

public Err(string m, int s)

{

msg = m;


severity = s;

}

}



//Err инкапсулира съобщението за грешка с кода за сериозността

//на грешката. Класът ErrInfo съдържа метода getErrInfo, който връща обект от тип Err:

class ErrInfo

{

string[] msg ={"Грешка при извеждане.", "Грешка при въвеждане.",



"Недостатъчно свободно място на диска.",

"Излизане извън диапазона на индексите в масива"};

int[] howBad = { 3, 3, 2, 4 };

public Err getErrInfo(int i)

{

if (i >= 0 && i < msg.Length)



return new Err(msg[i], howBad[i]);

return new Err("Въведен е непрaвилен код за грешка", 0);

}

}

class Demo



{

public static void Main()

{

ErrInfo err = new ErrInfo();



Err e;

e = err.getErrInfo(2);

e = err.getErrInfo(19);

}

}



+ малко обяснения по програмата – кое какво прави.
Каталог: files -> lekcii
files -> Рецептура на лекарствените форми рецептурни бланки и тяхната валидност
files -> Прогностични възможности на тестовете, използвани за подбор на млади футболисти
files -> Правила за реда за ползване, стопаниване и управление на стадион "христо ботев" благоевград глава първа общи положения
lekcii -> Е п и с к о п к о н с т а н т и н п р е с л а в с к и
lekcii -> Лекции на IV курс, уктц правец Съдържание Подпрограми в паскал 3 Дефиниране на подпрограми 3
lekcii -> C# изборна дисциплина


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




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

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