Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
NET(2).doc
Скачиваний:
1
Добавлен:
01.07.2025
Размер:
652 Кб
Скачать
☆

Краткие теоретические сведения

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

[атрибуты] [модификаторы]enum имя_перечисления [: базовый класс] {список_возможных_значений}

Особенности объявления перечислений:

  • перечисления могут быть объявлены непосредственно в про­странстве имен проекта или могут быть вложены в описание класса;

  • константы разных перечислений могут совпадать. Имя констан­ты всегда уточняется именем перечисления;

  • константы могут задаваться словами русского языка;

  • разрешается задавать базовый класс перечисления (любой цело­численный тип кроме long).

Например, для создания системы расчета заработной платы со­трудников можно использовать имена VP, Manager, Grunt и Contractor.

Значения по умолчанию:

enum ЕmрТуре { Manager, // = 0 Grunt, // = 1 Contractor, // = 2

VP // = 3

}

Можно задать значения:

enum EmpType {

Manager = 102,

Grunt, // = 103

Contractor, // = 104 VP // = 105

}

Структуры С# можно рассматривать как разновидность классов. Для структур можно определять конструкторы (только с параметрами), структуры могут реализовывать интерфейсы. Синтаксис объявления структуры:

[атрибуты] [модификаторы] struct имя_структуры [:список_интерфейсов] {тело_структуры}

Программа 1. Использование перечислений и структур в програм­мах на C#

enum EmpType : byte {

Manager = 10, Grunt = 1,

Contractor = 100, VP = 9

}

struct Employee {

public EmpType title; // Поле - перечисление public string name; public short deptID;

}

class StructTester {

public static int Main(string[] args) {

Employee fred; //Создание структурной переменной

fred.deptID = 40;

fred.name = "Fred";

fred.title = EmpType.Grunt;

return 0;

Для структур можно определить конструкторы с параметрами: struct Employee {

// Конструктор

public Employee (EmpType et, string n, short d) { title = et; name = n; deptID = d;

}}

Теперь можно создавать объекты сотрудников следующим образом:

class StructTester {

public static int Main(string[] args) {

Employee тагу =new Employee (EmpType.VP, "Mary", 10);

return 0;

}}

Классы являются расширением структурного типа. Синтаксис описания класса:

[атрибуты] [модификаторы]class имя_класса [:список_родителей] {тело_класса}

Модификаторы доступа к членам классов:

public - член доступен вне определения класса и иерархии произ­водных классов.

protected - член не видим за пределами класса, к нему могут обра­щаться только производные классы.

private - член не доступен за пределами области видимости опре­деляющего его класса.

internal - член видим только в пределах текущей единицы компи­ляции.

Обычно класс имеет модификатор доступа public, являющийся зна­чением по умолчанию. Модификаторы private и protected используются для вложенных классов.

В теле класса могут быть объявлены: константы, поля, конструкто­ры и деструкторы, методы, события, делегаты, вложенные классы (структуры, интерфейсы, перечисления).

class Employee { private string fullName; private int empID; private float currPay; public Employee() {}

public Employee(string fullName, int empID, float currPay)

{ this. fullName = fullName; this.empID = empID; this. currPay = currPay;

}

// Метод для увеличения зарплаты сотрудника public void GiveBonus(float amount)

{ currPay += amount; }

// Метод для вывода сведений о текущем состоянии объекта public virtual void DisplayStats() {

Console. WriteLine("Name: {0}", fullName); Console.WriteLine(“Pay: {0}", currPay);

Console.WriteLine(“ID: {0}", empID);

}

}

public static void Main() {

Employee e = new Employee(“Joe", 80, 30000); e.GiveBonus(200);

Employee e2;

e2 = new Employee("Beth", 81, 50000);

e2.GiveBonus(1000);

e2.DisplayStats();

}

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

Перечислим пять наиболее употребительных стратегий:

  • чтение, запись (Read, Write);

  • чтение, запись при первом обращении (Read, Write-once);

  • только чтение (Read-only);

  • только запись (Write-only);

  • ни чтения, ни записи (Not Read, Not Write).

Рассмотрим класс Person, у которого пять полей: fam, status, salary, age, health, характеризующих соответственно фамилию, статус, зарпла­ту, возраст и здоровье персоны. Для каждого из этих полей может быть своя стратегия доступа.

Программа 2. Свойства в класса на C#

public class Person {

string fam="", status-'", health-'"; int age=0, salary=0;

public string Fam { //стратегия: Read,Write-once

set {if (fam == "") fam = value;}

get {return(fam);}

}

public string Status { //стратегия: Read-only

get {return(status);}

}

public int Age { //стратегия: Read,Write

set { age - value;

if(age < 7) status -"ребенок"; else if(age <17)

status -"школьник";

else if (age < 22) status = "студент";

else status = "служащий";

}

get {return(age);}

}

public int Salary { //стратегия: Write-only

set {salary - value;}

}

}

public void TestPersonProps(){

Person persl = new Person();

persl.Fam = "Петров";

persl.Age = 21;

persl.Salary = 1000;

Console.WriteLine ("Фам={0}, возраст={1}, статус={2}", pers1.Fam, pers1.Age, pers1.Status);

pers1.Fam = "Иванов";

pers1.Age += 1;

Console.WriteLine ("Фам={0}, возраст={1}, статус={2}", pers1.Fam, pers1.Age, pers1.Status);

Индексатор. Обеспечивает доступ к закрытому полю, представ­ляющему массив. Объекты класса индексируются по этому полю. У класса может быть только один индексатор со стандартным име­нем this.

Добавим в класс Person массив children, задающий детей персоны, сделаем это свойство закрытым, а доступ к нему обеспечит индек­сатор:

Программа 3. Индексаторы в классе на C#

const int Child_Max = 10; //максимальное число детей

Person[] children = new Person[Child_Max]; int count_children=0; //текущее число детей объекта

public Person this[int i] { //индексатор

get {

if (i>=0 && i< count_children) return(children[i]); else return(children[0]);

}

set {

if (i==count_children && i< Child_Max) { children[i] = value; count_children++;

}

}}

public void TestPersonChildren(){

Person pers1 = new Person(), pers2 = new Person();

pers1.Fam = "Петров"; pers1.Age = 42;

pers1.Salary = 10000;

pers1[pers1.Count_children] = pers2;

pers2.Fam -'Петров";

pers2.Age = 21;

pers2.Salary = 1000;

Console.WriteLine ("Фам={0}, возраст={1}, статус={2}", pers1.Fam, pers1.Age, pers1.Status);

Console.WriteLine ("Сын={0}, возраст={1}, статус={2}", pers1[0].Fam, pers1[0].Age, pers1[0].Status);

}

Статические поля и методы класса. У класса могут быть поля, связанные не с объектами, а с классом. Эти поля объявляются как ста­тические с модификатором static. Статические поля доступны всем методам класса. Независимо от объекта используется одно и то же ста­тическое поле, позволяя использовать информацию, созданную други­ми объектами класса.

Аналогично полям у класса могут быть и статические методы, объ­явленные с модификатором static. Такие методы обрабатывают общую для класса информацию, хранящуюся в его статических полях.

Наследование. Повторное использование кода - это одна из глав­ных целей ООП. Класс-потомок наследует все возможности родитель­ского класса - все поля и все методы, открытую и закрытую часть класса. Единственное, что не наследует потомок - это конструкторы родительского класса. Конструкторы потомок должен создавать сам. В C# разрешено только одиночное наследование, т. е. у класса- потомка может быть только один родительский класс.

Рассмотрим класс Found, играющий роль родительского клаccа:

Программа 4. Родительский класс Found public class Found {

protected string name;

protected int credit;

public Found() { }

public Found(string name, int sum) { this.name = name; credit = sum;

}

public virtual void VirtMethod() { //виртуальный метод Console.WriteLine ("Отец: " + this.ToString() );

}

//переопределенный метод базового класса Object public override string ToString() {

return(String.Format("поля: name = {0}, credit = {1}", name, credit));

}

public void NonVirtMethod() {

Console.WriteLine ("Мать: " + this.ToString() );

}

public void Analysis() {

Console.WriteLine ("Простой анализ");

}

public void Work() {

VirtMethod();

NonVirtMethod();

Analysis();

}

Класс Found переопределил родительский метод класса Object ToString(), задав собственную реализацию возвращаемой методом строки. На переопределение родительского метода указывает модифи­катор override.

Создадим класс Derived - потомка класса Found. Потомок может добавить новые свойства - поля класса. Заметьте, потомок не может ни отменить, ни изменить модификаторы или типы полей, насле­дованных от родителя.

Класс Derived добавляет новое поле protected int debet.

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

public Derived(String name, int cred, int deb): Found (name,cred)

{}

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

Добавление методов и изменение методов родителя

Потомок может добавлять новые собственные методы.

Класс-потомок может изменять наследуемые им методы. Если по­томок создает метод с именем, совпадающим с именем метода пред­ков, то возможны три ситуации:

  • перегрузка метода. Она возникает, когда сигнатура создаваемо­го метода отличается от сигнатуры наследуемых методов предков;

  • переопределение метода. Метод родителя в этом случае должен иметь модификатор virtual или abstract. При переопределении сохра­няется сигнатура и модификаторы доступа наследуемого метода;

  • сокрытие метода. Если родительский метод не является вирту­альным или абстрактным, то потомок может создать новый метод с тем же именем и той же сигнатурой, скрыв родительский метод в дан­ном контексте. При вызове метода предпочтение будет отдаваться ме­тоду потомка. Скрытый родительский метод всегда может быть вы­зван, если его уточнить ключевым словом base. Метод потомка следует сопровождать модификатором new, указывающим на новый метод.

Программа 4. Класс наследник с переопределенными методами родителя

public class Derived:Found { protected int debet; public Derived() {}

public Derived(String name, int cred, int deb): Found (name,cred){ debet = deb;

}

public void DerivedMethod(){ //новый метод потомка Console.WriteLine("Это метод класса Derived");

}

new public void Analysis(){ //сокрытие метода родителя base.Analysis();

Console.WriteLine("Сложный анализ");

}

public void Analysis(int level){ // перегрузка метода

base.Analysis();

Console.WriteLine("Анализ глубины {0}", level);

}

public override String ToString(){ //переопределение метода

return(String.Format("поля: name = {0}, credit = {1},debet ={2}",name, credit, debet));

}

// переопределение метода родителя public override void VirtMethod() {

Console.WriteLine ("Сын: " + this.ToString() );

}

}

Интерфейсы. Интерфейс представляет собой полностью абстракт­ный класс, все методы которого абстрактны. Методы интерфейса объ­являются без указания модификатора доступа (по умолчанию public). Класс, наследующий интерфейс, обязан реализовать все методы ин­терфейса.

В языке C# полного множественного наследования классов нет. Чтобы частично сгладить этот пробел, допускается множественное на­следование интерфейсов.

Две стратегии реализации интерфейса. Опишем некоторый ин­терфейс, задающий дополнительные свойства объектов класса:

public interface IProps{ void Prop1(string s); void Prop2 (string name, int val);

}

  • Класс, наследующий интерфейс и реализующий его методы, мо­жет реализовать их явно, объявляя соответствующие методы класса открытыми (public).

  • Другая стратегия реализации состоит в том, чтобы некоторые методы интерфейса сделать закрытыми (private), уточнив имя метода именем интерфейса:

public class ClainP:IProps{ public ClainP(){ } void IProps .Prop1(string s) {

Console.WriteLine(s);

}

void IProps.Prop2(string name, int val) {

Console.WriteLine("name = {0}, val ={1}", name, val);

}

}

Есть два способа получения доступа к закрытым методам.

  • Обертывание. Создается открытый метод, являющийся оберт­кой закрытого метода.

  • Кастинг. Создается объект интерфейсного класса IProps, полу­ченный преобразованием (кастингом) объекта исходного класса ClainP. Этому объекту доступны закрытые методы интерфейса.

Вот пример обертывания закрытых методов в классе ClainP:

public void MyProp1(string s){

((IProps)this).Prop1(s);

}

public void MyProp2(string s, int x) {

((IProps)this).Prop2(s, x);

}

Методы переименованы и они получили другие имена, под кото­рыми и будут известны клиентам класса. В обертке для вызова закры­того метода пришлось использовать кастинг, приведя объект this к ин­терфейсному классу IProps.

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

public void TestClainIProps(){

Console.WriteLme("Объект класса ClainP вызывает открытые методы!");

ClainP clain = new Clain();

clain.Prop1(" свойство 1 объекта");

с1ат.Ргор2("Владимир", 44);

Console.WriteLine("Объект класса IProps вызывает открытые методы!");

IProps ip = (IProps)clain;

ip.Prop1("интерфейс: свойство");

ip.Prop2 ("интерфейс: свойство",77);

}

Проблемы множественного наследования. При множественном наследовании интерфейсов также возникают проблемы, но решение их становится проще. Рассмотрим две основные проблемы - коллизию имен и наследование от общего предка.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]