Класс (поля, методы и свойства, области видимости, 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Класс (поля, методы и свойства, области видимости,



разработчики и пользователи класса)

В языке С# для определения классов применяется ключевое слово class:

classMyClass

{ // Члены класса }

В этом коде определяется класс по имени MyClass. После определения класса его экземпляр можно создавать в любом месте проекта, где имеется доступ к этому определению. По умолчанию классы объявляются внутренними, что делает их доступными только для кода, который находится внутри текущего проекта.

Каждый класс имеет поля, которые используются для поддержания внутреннего состояния этого класса, операции (методы), которые определяют его поведение и свойства. У класса есть разработчик и пользователи. С внешней точки зрения (с точки зрения пользователя класса) класс содержит только методы, так как поля не должны быть доступны для пользователя. С точки зрения разработчика класса он содержит поля, свойства и методы. Таким образом, класс объединяет в себе (инкапсулирует) состояние и поведение. Пользователи могут узнать только о поведении класса - о его методах. Разработчикам доступна полная информация о том, как методы обеспечивают необходимое поведение и изменяют свое состояние. У каждого экземпляра класса имеются переменные экземпляра - его поля. Так как пользователям класса доступны только методы, значения этих переменных должны изменяться пользователями класса только с помощью методов класса.

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

public (открытый)

Доступ к члену возможен из любого другого кода в приложении и другом классе, ссылающемся на данный класс.

private (закрытый)

Доступ к члену можно получить только из кода в том же классе.

protected (защищенный)

Доступ к члену можно получить только из кода в том же классе или в производном классе.

Каждый раз, когда создается экземпляр класса или объект, вызывается конструктор. Класс может иметь несколько конструкторов, принимающих различные аргументы. Конструкторы позволяют программисту инициализировать объект и задавать значения по умолчанию.

Деструкторы используются для уничтожения экземпляров классов. Класс может иметь только один деструктор. Деструкторы невозможно вызвать. Они запускаются автоматически.

В примере показано объявление полей, конструкторов и методов классов. В нем также демонстрируется создание объекта и печать данных экземпляра. В этом примере объявляются два класса — класс Child, который содержит два закрытых поля (name и age) и два открытых метода. Второй класс, TestClass, используется для размещения функции Main.

classChild{ private int age;Private string name; // Конструктор по умолчаниюpublic Child(){ name = "N/A"; } // Конструкторpublic Child(string name, int age) { this.name = name;this.age = age; } // Методвыводаpublic void PrintChild() { Console.WriteLine("{0}, {1} years old.", name, age); }}classTestClass{ static void Main(){Создание объектов с помощью оператора newChild child1 = new Child("Craig", 11); Child child2 = new Child("Sally", 10);// Создание объекта с использованием конструктора по умолчаниюChild child3 = new Child(); // ВыводрезультатовConsole.Write("Child #1: ");child1.PrintChild();Console.Write("Child #2: ");child2.PrintChild();Console.Write("Child #3: ");child3.PrintChild(); }}

 

Свойства объединяют функции полей и методов. Для пользователя класса свойство является полем, поэтому для доступа к свойству требуется тот же синтаксис, что и для поля. Для разработчика класса свойство является одним или двумя блоками кода, представляющими метод доступа get и/или метод доступа set. Блок кода для метода доступа get выполняется, когда осуществляется чтение свойства; блок кода для метода доступа set выполняется, когда свойству присваивается новое значение. Свойство без метода доступа set считается доступным только для чтения. Свойство без метода доступа get считается доступным только для записи. Свойство с обоими методами доступа доступно для чтения и для записи. Свойство, как правило, является открытым. Для доступа к значению, присваиваемому свойству в методе set, используется value

 

В примере класс TimePeriod хранит сведения о периоде времени. Поле seconds хранит время в секундах, но свойство с именем Hours позволяет задать время в часах. Методы доступа для свойства Hours выполняют преобразование между часами и секундами.

classTimePeriod{ private double seconds;public double Hours { get { return seconds / 3600; }set { seconds = value * 3600; } }}class Program{ static void Main() { TimePeriod t = new TimePeriod(); // Присвоениесвойству Hours значения t.Hours = 24; // Получение значения свойства Hours.System.Console.WriteLine("Time in hours: " + t.Hours); }}

Инкапсуляция, наследование и полиморфизм

Как свойства класса

Класс имеет три важных свойства: инкапсуляция, наследование и полиморфизм.

Инкапсуляция связана с областями видимости членов класса. Будем понимать под инкапсуляцией доступность объекта исключительно посредством его свойств и методов. Другими словами, концепция инкапсуляции призвана обеспечивать безопасность проектирования и реализации программы на основе локализации манипулирования объектом в областях его полей и методов.

Иначе говоря, полями объекта возможно оперировать исключительно посредством его методов. Практическая важность концепции инкапсуляции для языка C#) определяется следующими фундаментальными свойствами.

Прежде всего, реализация концепции инкапсуляции обеспечивает совместное хранение данных (полей) и функций (методов) внутри объекта.

Наследование (inheritance) является одним из самых важных механизмов в ООП. Любой класс может наследоваться от другого класса, а это значит, что он будет иметь все те же члены, что и класс, от которого он унаследован. В терминологии ООП класс, от которого наследуется другой класс (или, другими словами, создается производный класс), называется родительским или базовым классом. Важно обратить внимание на то, что в С# напрямую наследоваться классы могут только от одного базового класса, хотя у базового класса может быть свой собственный базовый класс и т.д.

Механизм наследования позволяет расширять или создавать специфические классы от одного более общего базового класса. Например, возьмем класс, представляющий животное с фермы. Этот класс мог бы называться Animal и обладать методами вроде EatFood () (Кормить) или Breed () (Разводить).

Можно создать производный класс по имени Cow (Корова), который бы поддерживал все те же самые методы, но при этом также имел и свои собственные, например, Моо() (Мычать) и SupplyMilk() (Давать молоко), а также еще один производный класс по имени Chicken (Курица) с методами Cluck() (Кудахтать) и LayEgg() (Снести яйцо).

При применении наследования от базового класса важным становится вопрос о доступности членов. Закрытые члены базового класса не являются доступными для производного класса, а открытые — являются. Однако открытые члены доступны как для производного класса, так и для внешнего кода. Поэтому если бы использовать можно было только два таких уровня доступности, создание члена, доступного как для базового, так и для производного класса, но не для внешнего кода, оказалось бы невозможным.

Для исключения вероятности возникновения подобной проблемы существует еще и третий уровень доступности — protected, при котором доступ к члену имеют только производные классы. Что касается внешнего кода, то для него член с уровнем доступности protected идентичен члену с уровнем private.

Помимо уровня защиты для члена еще также может определяться и поведение, связанное с наследованием. Члены базового класса могут быть виртуальными (virtual), а это означает то, что они могут переопределяться в классе, который их наследует.

Другими словами, в производном классе для членов может предоставляться альтернативная реализация. Если никакой альтернативной реализации не предоставлено, тогда любой внешний код, получающий доступ к члену через производный класс, автоматически использует реализацию этого члена, которая содержится в базовом классе.

Виртуальные члены не могут быть закрытыми, поскольку это приводит к парадоксу: нельзя говорить, что член может переопределяться в производном классе и одновременно утверждать, что он не должен быть доступен в производном классе.

В примере с классом Animal можно было бы сделать виртуальным метод EatFood () и предоставить для него новую реализацию в каком-то производном классе, например, в Cow.

Одним из результатов наследования является наличие в классах, унаследованных от базового, некоторых общих возможностей с ним (полей и методов).

Благодаря этому зачастую оказывается возможным применять для объектов, которые как экземпляры классов с общим базовым типом, имеют идентичный синтаксис. Например, при наличии у класса Animal метода по имени EatFood () синтаксис для вызова этого метода из объектов производных классов Cow и Chicken будет выглядеть одинаково:

Cow myCow = new Cow();

Chicken myChicken = new Chicken ();

myCow.EatFood();

myChicken.EatFood();

Полиморфизм (polymorphism) позволяет присваивать переменной базового типа переменную одного из производных типов, как показано ниже:

AnimalmyAnimal = myCow;

Никакого приведения типов выполнять не требуется. Далее можно просто вызывать методы базового класса через эту переменную:

myAnimal.EatFood();

Данная строка кода приведет к вызову реализации метода EatFood (), содержащейся в производном классе. Важно обратить внимание на то, что вызывать подобным образом методы, определенные в производном классе, нельзя. То есть следующий код работать не будет:

myAnimal.Moo ();

Однако приводить переменную типа базового класса к переменной типа производного класса и вызывать метод производного класса показанным ниже образом можно:

Cow myNewCow = (Cow)myAnimal;

myNewCow.Moo();

Полиморфизм является чрезвычайно полезной технологией для решения задач с использованием минимального количества кода в разных объектах - потомках от одного единственного класса. Следует отметить, что полиморфизм может применяться не только в отношении классов, имеющих одинаковый родительский класс. Он точно так же может применяться и, скажем, в отношении дочерних и "внучатых" классов, главное, чтобы в их иерархии наследования присутствовал какой-то общий класс.

Пример программирования классов

Рассмотрим программирование классов на примере. Пусть имеется базовый класс stud (Студент) и класс-потомок grad_stud (Студент-выпускник). Диаграмма классов имеет вид (рис.)

Рис. Диаграмма классов примера

 

Эту диаграмму можно увидеть в VisualStudio.

Поля базового класса: name (Имя), age (Возраст), ball (Балл), group (Группа). У класса-потомка дополнительно есть поле place (Место распределения). Каждому полю соответствует общедоступное свойство. Метод info(), который присутствует в каждом классе, возвращает информацию о полях класса в виде строки, но работает в каждом классе по-своему, так как в каждом классе неодинаковый набор полей. Поэтому этот метод будет виртуальным.

Особо нужно сказать о методах, имена которых совпадают с именами классов.

Базовая инициализация объекта осуществляется автоматически. Однако иногда бывает необходимо, чтобы на стадии инициализации объекта выполнялись какие-то дополнительные задачи, например, инициализация хранимых объектом данных. Для осуществления подобного применяется функция-конструктор. Все объекты имеют конструктор по умолчанию, который представляет собой не принимающий параметров метод с таким же именем, как у самого класса. Определение класса может также включать несколько методов-конструкторов, принимающих параметры и отличных от конструктора по умолчанию. Такие конструкторы позволяют создавать экземпляр объекта различными способами, например, предоставляя начальные значения для хранимых в объекте данных.

В языке С# конструкторы вызываются с использованием ключевого слова new. Например, создать экземпляр объекта CupOfCoffee с применением конструктора по умолчанию можно было бы следующим образом:

CupOfCoffeemyCup = newCupOfCoffee ();

Экземпляры объектов могут также создаваться и с помощью конструкторов не по умолчанию. Например, у класса CupOfCoffee мог бы существовать конструктор не по умолчанию, принимающий параметр для установки во время создания экземпляра типа кофейных зерен:

CupOfCoffeemyCup = new CupOfCoffee("Blue Mountain");

Деструкторы используются для выполнения очистки после удаления объектов. В целом предоставлять код для метода-деструктора не требуется; это делается автоматически.

Описание классов примера:

public class stud

{

private string name;

privateint age;

privateint ball;

privateint group;

public string Name

{

get { return name; }

set { name = value; }

}

publicint Group

{

get { return group; }

set { if (value>0&&value<50)

group = value; }

}

publicint Age

{

get { return age; }

set

{ if (value > 0 && value < 80)

age = value; }

}

publicint Ball

{

get { return ball; }

set

{ if (value > 0 && value < 100)

ball = value; }

}

public stud(){}

public stud (string aname, int aage, int aball,int agr)

{Name=aname;Age=aage;Ball=aball;Group=agr;}

public virtual string info() { string X;

X = Name + " " + Convert.ToString(Age) + " " +

Convert.ToString(Ball) + " " + Convert.ToString(Group);

return X;

}

}

public class grad_stud: stud

{

private string place;

public string Place

{

get { return place; }

set { place = value; }

}

public grad_stud(){}

public grad_stud (string aname, int aage, int aball, int agr, string aplace):

base(aname, aage, aball, agr)

{ Place = aplace; }

public override string info()

{

string X;

X = base.info() + " " + Place;

return X;

}

}

Описание класса начинается словом class.

В описании класса-потомка grad_stud после двоеточия указывается имя класса-предка. Конструктор класса-потомка после двоеточия содержит вызов конструктора класса-предка (слово base), а в теле метода – инициализацию собственного поля. Метод info() предка содержит слово virtual, которое означает, что этот метод будет переопределен в потомке. Метод info() потомка содержит слово override, которое означает, что этот метод переопределяет одноименный метод предка; метод вначале вызывает одноименный метод предка, а затем присоединяет собственное поле.

В языке С# предусмотрен один общий базовый класс для всех объектов, имеющий имя object. Его метод GetType() может быть полезен при использовании полиморфизма, поскольку позволяет выполнять различные операции с объектами в зависимости от их типа, а не одну и ту же операцию для всех объектов, как это часто бывает. Например, при наличии функции, принимающей параметр типа object (это означает, что ей можно передавать практически все что угодно), можно было бы сделать так, чтобы в случае обнаружения объектов определенного типа выполнялись дополнительные задачи. В частности, с использованием комбинации из метода GetType и операции typeof (которая представляет собой операцию С#, преобразующую имя класса в объект System.Type) можно было бы обеспечить выполнение операции сравнения, как показано ниже:

if (myObj.GetType () == typeof(MyComplexClass))

{// myObjявляетсяэкземпляромклассаMyComplexClass. }

Пример приложения, использующего классы

Рассмотрим приложение, основанное на рассмотренной иерархии классов. В приложение поместим главную форму, форму добавления экземпляров классов, форму просмотра всех экземпляров классов и форму запроса.

На главной форме поместим меню (рис.):

Рис. Главная форма приложения

Кроме двух классов иерархии, создадим класс Global для размещения полей, видимых всем формам:

class Global

{

public struct st

{

public string name, place;

public int age, ball, group;

};

public static st st1;

public static stud A1=new stud();

public static stud A2 = new stud();

public static grad_stud B1=new grad_stud();

public static grad_stud B2 = new grad_stud();

public static FileStream aFile;

public static StreamWriter sw;

public static StreamReader sr; }

Форма добавления (рис.)

Рис. Форма добавления студента

На форме есть компонента groupBox, содержащая две компоненты radioButton для выбора типа добавляемого объекта, при выполнении события radioButton1_CheckedChanged поля, связанные с местом распределения, становятся невидимы; для аналогичного события для второй кнопки они становятся видимыми:

private void radioButton1_CheckedChanged(object sender, EventArgs e)

{ label5.Visible = false;

textBox1.Visible = false; }

private void radioButton2_CheckedChanged(object sender, EventArgs e)

{ label5.Visible = true;

textBox1.Visible = true; }

После заполнения необходимых полей в зависимости от нажатой кнопки создается и инициализируется определенный экземпляр определенного класса, например, при нажатии кнопки «Первый студент» выполняется код:

private void button5_Click(object sender, EventArgs e)

{Global.st1.name=textBox2.Text;

Global.st1.age = Convert.ToInt32(maskedTextBox1.Text);

Global.st1.ball = Convert.ToInt32(maskedTextBox2.Text);

Global.st1.group = Convert.ToInt32(maskedTextBox3.Text);

Global.A1 = new stud(Global.st1.name, Global.st1.age, Global.st1.ball,

Global.st1.group); }

Заполняются поля структуры и инициализируктся экземпляр Global.A1.

Форма просмотра содержит компоненту ListBox; при загрузке формы выполняется код для добавления в список значений метода Info() для каждого объекта:

private void Form3_Load(object sender, EventArgs e)

{ listBox1.Items.Clear();

if (Global.A1!=null)

listBox1.Items.Add(Global.A1.info());

if (Global.A2!=null)listBox1.Items.Add(Global.A2.info());

if (Global.B1!=null)listBox1.Items.Add(Global.B1.info());

if (Global.B2!=null)listBox1.Items.Add(Global.B2.info()); }

Форма запроса имеет вид (рис.):

Рис. Форма просмотра

При нажатии кнопки выполняется код:

private void button1_Click(object sender, EventArgs e)

{int cnt=0;

if (Global.B1.get_place() == textBox1.Text)

cnt++;

if (Global.B2.get_place() == textBox1.Text)

cnt++;

MessageBox.Show(Convert.ToString(cnt)); Close(); }

Код записи объектов в файл:

Global.aFile = new FileStream("stud.txt", FileMode.Create);

Global.sw = new StreamWriter(Global.aFile);

Global.sw.Close();

Global.aFile = new FileStream("stud.txt", FileMode.Append);

Global.sw = new StreamWriter(Global.aFile);

// Записатьданныевфайл

 

Global.sw.WriteLine("{0},{1},{2},{3},{4}", Global.A1.GetType(), Global.A1.Name, Global.A1.Age,

Global.A1.Ball,Global.A1.Group);

Global.sw.WriteLine("{0},{1},{2},{3},{4}", Global.A2.GetType(), Global.A2.Name, Global.A2.Age,

Global.A2.Ball, Global.A2.Group);

Global.sw.WriteLine("{0},{1},{2},{3},{4},{5}", Global.B1.GetType(), Global.B1.Name, Global.B1.Age,

Global.B1.Ball, Global.B1.Group,Global.B1.Place);

Global.sw.WriteLine("{0},{1},{2},{3},{4},{5}", Global.B2.GetType(), Global.B2.Name, Global.B2.Age,

Global.B2.Ball, Global.B2.Group,Global.B2.Place);

Global.sw.Close();

Код чтения объектов из файла:

stringlin,ob;

char[] separator = new char[] { ',' };

string[] slov = new string[6];

Global.aFile = new FileStream("stud.txt", FileMode.Open);

Global.sr = new StreamReader(Global.aFile);

Global.A1 = new stud();

Global.A2 = new stud();

Global.B1 = new grad_stud();

Global.B2 = new grad_stud();

 

lin = Global.sr.ReadLine();

slov = lin.Split(separator);

ob=slov[0];

if (ob == ("WindowsFormsApplication2.stud"))

{Global.A1.Name=slov[1];

Global.A1.Age=Convert.ToInt32(slov[2]);

Global.A1.Ball=Convert.ToInt32(slov[3]);

Global.A1.Group=Convert.ToInt32(slov[4]);

}

lin = Global.sr.ReadLine();

slov = lin.Split(separator);

Global.A2.Name=slov[1];

Global.A2.Age=Convert.ToInt32(slov[2]);

Global.A2.Ball=Convert.ToInt32(slov[3]);

Global.A2.Group=Convert.ToInt32(slov[4]);

lin = Global.sr.ReadLine();

slov = lin.Split(separator);

Global.B1.Name=slov[1];

Global.B1.Age=Convert.ToInt32(slov[2]);

Global.B1.Ball=Convert.ToInt32(slov[3]);

Global.B1.Group=Convert.ToInt32(slov[4]);

Global.B1.Place=slov[5];

lin = Global.sr.ReadLine();

slov = lin.Split(separator);

Global.B2.Name=slov[1];

Global.B2.Age=Convert.ToInt32(slov[2]);

Global.B2.Ball=Convert.ToInt32(slov[3]);

Global.B2.Group=Convert.ToInt32(slov[4]);

Global.B2.Place=slov[5];

Global.sr.Close();

 



Поделиться:


Последнее изменение этой страницы: 2016-09-19; просмотров: 456; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.222.182.105 (0.117 с.)