Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Shpory_OOP.doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
2.17 Mб
Скачать

Цикл foreach

Новым видом цикла, не унаследованным от С++, является цикл foreach, удобный при работе с массивами, коллекциями и другими подобными контейнерами данных. Его синтаксис:

foreach(тип идентификатор in контейнер) оператор

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

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

Серьезным недостатком циклов foreach в языке C# является то, что цикл работает только на чтение, но не на запись элементов. Так что наполнять контейнер элементами приходится с помощью других операторов цикла.

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

/// Демонстрация цикла foreach. Вычисление суммы,

/// максимального и минимального элементов

/// трехмерного массива, заполненного случайными числами.

public void SumMinMax()

{ int [,,] arr3d = new int[10,10,10];

Random rnd = new Random();

for (int i =0; i<10; i++)

for (int j =0; j<10; j++)

for (int k =0; k<10; k++)

arr3d[i,j,k]= rnd.Next(100);

long sum =0; int min=arr3d[0,0,0], max=arr3d[0,0,0];

foreach(int item in arr3d)

{ sum +=item;

if (item > max) max = item;

else if (item < min) min = item; }

Console.WriteLine("sum = {0}, min = {1}, max = {2}",

sum, min, max);

}//SumMinMax

25. Классы, создание объектов.

Формальное определение класса в С#

Класс в С#, как и в других языках программирования, — это пользовательский

тип данных (user defined type, UDT), который состоит из данных (часто называе-

мых атрибутами или свойствами) и функциями для выполнения с этими данными

различных действий (эти функции обычно называются методами). Классы позво-

ляют группировать в единое целое данные и функциональность, моделируя объек-

ты реального мира. Именно это свойство классов и обеспечивает одно из наиболее

важных преимуществ объектно-ориентированных языков программирования.

К примеру, предположим, что нам потребовалось создать модель сотрудника

нашей организации. Конечно же, для этой цели удобнее всего создать специаль-

ный класс. Этот класс, как минимум, должен хранить данные об имени работника,

его идентификационном номере и текущей заработной плате. Помимо этого, пусть

в нашем классе будут определены два метода — GiveBonusQ для увеличения зара-ботной платы сотрудника и 01 spl ayStats {) для вывода всех имеющихся данных об

этом сотруднике (рис. 3.1).

Как уже говорилось в главе 2, для классов С# можно определить любое количе-

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

ты данного класса, настраивая при этом их исходное состояние нужным вам обра-

зом. Любой класс С# автоматически снабжается конструктором по умолчанию (не

принимающим никаких параметров). Этот конструктор в С# (в отличие от C++)

при создании объекта автоматически присвоит всем переменным-членам класса

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

конструктор по умолчанию, так и создать такое количество пользовательских кон-

структоров (принимающих параметры), сколько вам необходимо. Давайте опре-

делим наш класс для сотрудника на С#:

// Исходное определение класса

class Employee

{

// Внутренние закрытые данные класса

private string fullName;

private int erapID;

private float currPay;

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

public EmployeeO {}

public EmployeeCstring fullName,

{

this. fullName = fullName;

this.empID = empID;

this. currPay = currPay:

int empID. float currPay)

// Метод для увеличения зарплаты сотрудника

public void CiveBonusffloat amount)

{ currPay += amount; }

// Метод для вывода сведений о текущем состоянии объекта

public virtual void DisplayStatsO

Формальное определение класса в С# 141

Console. WriteUnet"Name: {0}", full Name):

Console.WriteLineC'Pay: {0}". currPay):

Console.UrtteLineCID: {0}". empID);

Console.WriteLineC'SSN: {0}". ssn);

}

Обратите внимание на то, что определение для конструктора по умолчанию не

содержит каких-либо действий, которые должен выполнять конструктор:

class Employee

{

// Всей переменнын-членан значения по умолчанию будут присвоены автоматически

public EmployeeО {}

Необходимо обязательно помнить о следующем обстоятельстве: в С# (кик

и в C++), если вы определили хотя бы один пользовательский конструктор (при-

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

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

нения такого, к примеру, кода:

// Вызываем конструктор по умолчанию

Employee e = new EmployeeO:

вы получите сообщение об ошибке компилятора.

Использование пользовательского конструктора очевидно:

// Выэываен пользовательский конструктор (двуня способами)

public static void MainO

i

Employee e = new EmployeeC'Joe", 80, 30000);

e.GiveBonusC200):

Employee e2:

e2 - new Employee("Beth". 81. 50000);

e2.GiveBonus(1000);

e2.DisplayStats():

}

Код приложения Employees (с которым мы будем работать в продолжение всей

этой главы) можно найти в подкаталоге Chapter 3.

26. Методы класса и их параметры.

27. Свойства класса.

Второй способ инкапсуляции: применение

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

Помимо традиционных методов доступа и изменения для обращения к закрытым чле-

нам класса можно также использовать свойства (properties). В Visual Basic и СОМ

свойства — это привычный инструмент. Свойства позволяют имитировать доступ

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

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

переменной. Но на самом деле любое свойство состоит из двух скрытых внутренних

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

вать два отдельных метода, пользователь класса может использовать единствен» >е

свойство, работая с ним так же, как и с открытой переменной-членом данного класса:

// Обращение к имени сотрудника через свойство

public static int Main(sthng[] args)

Employee p = new EmployeeC);

// Устанавливаем значение

p.EmpIO = 81;

// Получаем значение

Console.WriteLineC'Person ID Is: {0}". p.EmpID):

return 0;

|Если заглянуть внутрь определения класса, то свойства всегда отображаются в

«реальные» методы доступа и изменения. А уже в определении этих методов вы

можете реализовать любую логику (например, для устранения лишних символов,

проверки допустимости вводимых числовых значений и прочего). Ниже представ-

лен синтаксис класса Employee с определением свойства EmpID:

// Пользовательское свойство EmpID для доступа к переменной етрШ

public class Employee{

private Int етрШ:

// Свойство для empID

public Int EmpID

{get {return empIO:}

set

{// Здесь вы можете реализовать логику для проверки вводимых

// значений и выполнения других действий

empID - value;} .}

Свойство С# состоит из двух блоков — блока доступа (get block) и блока изме-

нения (set block). Ключевое слово val ue представляет правую часть выражения при

присвоении значения посредством свойства. Как и все в С#, то, что представлено

словом val ue — это также объект. Совместимость того, что передается свойству как

value, с самим свойством, зависит от определения свойства. Например, свойство

EmpID предназначено (согласно своему определению в классе) для работы с закры-

тым целочисленным empID, поэтому число 81 вполне его устроит:

// В данной случае типом данных, используемым для value, будет Int

еЗ.EmpID - 81:

Показать дополнительные возможности применения ключевого слова value

можно на таком примере:

11 Свойство для ешрЮ

public Int EmpID {

get {return empID:}

set

Средства инкапсуляции в С# 153

// Как еще можно использовать value

Console.WnteLlneC'value is the Instance of: {0}". value.GetTypeO):

Console.WriteLineC'value as string: {0}". value.ToStrlngO);

erapID = value;

Результат работы данной программы представлен на рис. 3.7.

•- ^ D:\CSharp8ook\Labs\Chapter 3\Cmploye_. НИ*

иа.1»е is an instance of :Int32

jalue as string :81

Press any Itey to continue

Рис. 3.7. Значение «value» при EmpID = 81

Необходимо отметить, что обращаться к объекту v a l u e можно только в преде-

лах программного блока set внутри определения свойства. Попытка обратиться к

этому объекту из любого другого места приведет к ошибке компилятора.

Последнее, что мы отметим — использование свойств (по сравнению с тради-

ционными методами доступа и изменения) делает применение ваших типов более

простым. Например, предположим, что в классе Employee предусмотрена внутрен-

няя закрытая переменная для хранения информации о возрасте сотрудника. Вы

хотите, чтобы при наступлении дня рождения этого сотрудника значение этой пе-

ременной увеличивалось на единицу. При использовании традиционных методов

доступа и изменения эта операция будет выглядеть так:

Employee joe * new EmployeeO;

joe.SetAge( joe.GetAgeO + 1):

Используя свойство, вы можете сделать РТО проще:

Employee joe = new EmployeeO;

joe.Ag&t+:

Внутреннее представление свойств С#

Многие программисты стараются использовать для имен методов доступа и изме -

нения соответственно приставки get_ и set_ (например, get_Name() и set_Name()).

Само по себе это не представляет проблемы. Проблему представляет другое: С#

для внутреннего представления свойства использует методы с теми же самыми

префиксами. К примеру, если вы откроете сборку EmpLoyees.exe при помощи ути-

литы ILDastn.exe, вы увидите, что каждому свойству соответствуют два отдельных;

(и скрытых) метода (рис. 3.8).

Поэтому подобное определение класса вызовет ошибку компилятора:

Рис. З.8. Свойства отображаются в скрытые методы get_ и set_

Свойства только для чтения, только для записи

и статические

Наш рассказ о свойствах классов С# будет неполон, если мы не упомянем еще

о некоторых связанных с ними моментах. Как мы помним, наше свойство EmpID

было создано как свойство, доступное и для чтения, и для записи. Однако при со-

Средства инкапсуляции в С# 155

здании пользовательских свойств класса часто возникает необходимость создать

свойство, которое будет доступно только для чтения. Делается это очень просто:

необходимо в определении свойства пропустить блок set. В результате свойство

станет доступным только для чтения:

public class Employee

// Будем считать, что исходное значение этой переменной присваивается с помощью

// конструктора класса

private string ssn;

// А вот так выглядит свойство только для чтения

public string SSN ( get { return ssn: } }

C# также поддерживает статические свойства. Как мы помним, статические

переменные предназначены для хранения информации на уровне всего класса, а не

его отдельных объектов. Если у нас объявлены статические данные (то есть те же

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

свойства. Предположим, что в нашем классе Employee мы собираемся, помимо все-

го прочего, хранить еще и информацию об имени организации, в которой работают

все сотрудники — объекты класса Employee. Для этого будет использована специ-

альная статическая переменная. Соответствующее статическое свойство для ра-

боты с этой переменной может выглядеть так:

// Со статическими данными должны работать статические свойства

27. Свойства класса.

Второй способ инкапсуляции: применение свойств класса. Помимо традиционных методов доступа и изменения для обращения к закрытым членам класса можно также использовать свойства (properties). В Visual Basic и СОМ свойства — это привычный инструмент. Свойства позволяют имитировать доступ к внутренним данным класса: при получении информации или внесении изменений через свойство синтаксис выглядит так же, как при обращении к обычной открытой переменной. Но на самом деле любое свойство состоит из двух скрытых внутренних методов. Преимущество свойств заключается в том, что вместо того, чтобы использовать два отдельных метода, пользователь класса может использовать единствен» >е свойство, работая с ним так же, как и с открытой переменной-членом данного класса:

// Обращение к имени сотрудника через свойство

public static int Main(sthng[] args)

Employee p = new EmployeeC);

// Устанавливаем значение

p.EmpIO = 81;

// Получаем значение

Console.WriteLineC'Person ID Is: {0}". p.EmpID):

return 0;

|Если заглянуть внутрь определения класса, то свойства всегда отображаются в «реальные» методы доступа и изменения. А уже в определении этих методов вы можете реализовать любую логику (например, для устранения лишних символов, проверки допустимости вводимых числовых значений и прочего). Ниже представлен синтаксис класса Employee с определением свойства EmpID:

// Пользовательское свойство EmpID для доступа к переменной етрШ

public class Employee

{

private Int етрШ:

// Свойство для empID

public Int EmpID

{get {return empIO:}

set

{// Здесь вы можете реализовать логику для проверки вводимых

// значений и выполнения других действий

empID - value;

} .}

Свойство С# состоит из двух блоков — блока доступа (get block) и блока изменения (set block). Ключевое слово val ue представляет правую часть выражения при присвоении значения посредством свойства. Как и все в С#, то, что представлено словом val ue — это также объект. Совместимость того, что передается свойству как value, с самим свойством, зависит от определения свойства. Например, свойство EmpID предназначено (согласно своему определению в классе) для работы с закрытым целочисленным empID, поэтому число 81 вполне его устроит:

// В данной случае типом данных, используемым для value, будет Int

еЗ.EmpID - 81:

Показать дополнительные возможности применения ключевого слова value можно на таком примере:

11 Свойство для ешрЮ

public Int EmpID

{

get {return empID:}

set

Средства инкапсуляции в С# 153

// Как еще можно использовать value

Console.WnteLlneC'value is the Instance of: {0}". value.GetTypeO):

Console.WriteLineC'value as string: {0}". value.ToStrlngO);

erapID = value;

Результат работы данной программы представлен на рис. 3.7.

•- ^ D:\CSharp8ook\Labs\Chapter 3\Cmploye_. НИ*

иа.1»е is an instance of :Int32

jalue as string :81

Press any Itey to continue

Рис. 3.7. Значение «value» при EmpID = 81

Необходимо отметить, что обращаться к объекту v a l u e можно только в пределах программного блока set внутри определения свойства. Попытка обратиться к этому объекту из любого другого места приведет к ошибке компилятора. Последнее, что мы отметим — использование свойств (по сравнению с традиционными методами доступа и изменения) делает применение ваших типов более простым. Например, предположим, что в классе Employee предусмотрена внутренняя закрытая переменная для хранения информации о возрасте сотрудника. Вы хотите, чтобы при наступлении дня рождения этого сотрудника значение этой переменной увеличивалось на единицу. При использовании традиционных методов доступа и изменения эта операция будет выглядеть так:

Employee joe * new EmployeeO;

joe.SetAge( joe.GetAgeO + 1):

Используя свойство, вы можете сделать РТО проще:

Employee joe = new EmployeeO;

joe.Ag&t+:

Внутреннее представление свойств С#

Многие программисты стараются использовать для имен методов доступа и изменения соответственно приставки get_ и set_ (например, get_Name() и set_Name()). Само по себе это не представляет проблемы. Проблему представляет другое: С# для внутреннего представления свойства использует методы с теми же самыми префиксами. К примеру, если вы откроете сборку EmpLoyees.exe при помощи утилиты ILDastn.exe, вы увидите, что каждому свойству соответствуют два отдельных; (и скрытых) метода (рис. 3.8).

Поэтому подобное определение класса вызовет ошибку компилятора:

Рис. З.8. Свойства отображаются в скрытые методы get_ и set_

Свойства только для чтения, только для записи и статические. Наш рассказ о свойствах классов С# будет неполон, если мы не упомянем еще о некоторых связанных с ними моментах. Как мы помним, наше свойство EmpID было создано как свойство, доступное и для чтения, и для записи.

Средства инкапсуляции в С# 155 здании пользовательских свойств класса часто возникает необходимость создать свойство, которое будет доступно только для чтения. Делается это очень просто: необходимо в определении свойства пропустить блок set. В результате свойство станет доступным только для чтения:

public class Employee

// Будем считать, что исходное значение этой переменной присваивается с помощью

// конструктора класса

private string ssn;

// А вот так выглядит свойство только для чтения

public string SSN ( get { return ssn: } }

C# также поддерживает статические свойства. Как мы помним, статические переменные предназначены для хранения информации на уровне всего класса, а не его отдельных объектов. Если у нас объявлены статические данные (то есть те же переменные), то обращаться к ним и устанавливать значения должны статические свойства. Предположим, что в нашем классе Employee мы собираемся, помимо всего прочего, хранить еще и информацию об имени организации, в которой работают все сотрудники — объекты класса Employee. Для этого будет использована специальная статическая переменная. Соответствующее статическое свойство для а боты с этой переменной может выглядеть так:

// Со статическими данными должны работать статические свойства

28. Наследование и виртуальные функции.

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