Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C# - лекции IntUit (Биллиг В.А.).pdf
Скачиваний:
140
Добавлен:
13.02.2015
Размер:
4.13 Mб
Скачать

Что нужно знать о методах?

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

Почему у методов мало аргументов?

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

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

Поля класса или функции без аргументов?

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

Если бы синтаксис описания метода допускал отсутствие скобок у функции (метода), в случае, когда список аргументов отсутствует, то клиент класса мог бы и не знать, обращается ли он к полю или к методу. Такой синтаксис принят, например, в языке Eiffel. Преимущество этого подхода в том, что изменение реализации никак не сказывается на клиентах класса. В языке C# это не так. Когда мы хотим получить длину строки, то пишем s.Length, точно зная, что Length - это поле, а не метод класса String. Если бы по каким-либо причинам разработчики класса String решили изменить реализацию и заменить поле Length соответствующей функцией, то ее вызов имел бы

вид s.Length().

Пример: две версии класса Account

Проиллюстрируем рассмотренные выше вопросы на примере проектирования классов Account и Account1, описывающих такую абстракцию данных, как банковский счет. Определим на этих данных две основные операции - занесение денег на счет и снятие денег. В первом варианте - классе Account - будем активно использовать поля класса. Помимо двух основных полей credit и debit, хранящих приход и расход счета, введем поле balance, которое задает текущее состояние счета, и два поля, связанных с последней выполняемой операцией. Поле sum будет хранить сумму денег текущей

операции, а поле result - результат выполнения операции. Полей у класса много, и как следствие, у методов класса аргументов будет немного. Вот описание нашего

класса:

///<summary>

///Класс Account определяет банковский счет. Простейший

///вариант с возможностью трех операций: положить деньги

///на счет, снять со счета, узнать баланс.Вариант с полями

///</summary>

public class Account

{

//закрытые поля класса

int debit=0, credit=0, balance =0; int sum =0, result=0;

///<summary>

///Зачисление на счет с проверкой

///</summary>

///<param name="sum">зачисляемая сумма</param> public void putMoney(int sum)

{

this.sum = sum; if (sum >0)

{

credit += sum; balance = credit - debit; result =1;

}

else result = -1; Mes();

}//putMoney

///<summary>

///Снятие со счета с проверкой

///</summary>

///<param name="sum"> снимаемая сумма</param>

public void getMoney(int sum)

{

this.sum = sum; if(sum <= balance)

{

debit += sum; balance = credit - debit; result =2;

}

else result = -2; Mes();

}//getMoney

///<summary>

///Уведомление о выполнении операции

///</summary>

void Mes()

{

switch (result)

{

case 1:

Console.WriteLine("Операция зачисления денег прошла успешно!");

Console.WriteLine("Cумма={0},

Ваш текущий баланс={1}",sum, balance);

break; case 2:

Console.WriteLine("Операция снятия денег прошла успешно!");

Console.WriteLine("Cумма={0},

Ваш текущий баланс={1}", sum,balance); break;

case -1:

Console.WriteLine("Операция зачисления денег

не выполнена!");

Console.WriteLine("Сумма должна быть больше нуля!"); Console.WriteLine("Cумма={0},

Ваш текущий баланс={1}", sum,balance); break;

case -2:

Console.WriteLine("Операция снятия денег не выполнена!");

Console.WriteLine("Сумма должна быть не больше баланса!");

Console.WriteLine("Cумма={0},

Ваш текущий баланс={1}", sum,balance); break;

default:

Console.WriteLine("Неизвестная операция!"); break;

}

}

}//Account

Как можно видеть, только у методов getMoney и putMoney имеется один входной аргумент. Это тот аргумент, который нужен по сути дела, поскольку только клиент может решить, какую сумму он хочет снять или положить на счет. Других аргументов у методов класса нет - вся информация передается через поля класса. Уменьшение числа аргументов приводит к повышению эффективности работы с методами, так как исчезают затраты на передачу фактических аргументов. Но за все надо платить. В данном случае, усложняются сами операции работы со вкладом, поскольку нужно в момент выполнения операции обновлять значения многих полей класса. Закрытый метод Mes вызывается после выполнения каждой операции, сообщая о том, как прошла операция, и информируя клиента о текущем состоянии его баланса.

А теперь спроектируем аналогичный класс Account1, отличающийся только тем, что у него будет меньше полей. Вместо поля balance в классе появится соответствующая функция с этим же именем, вместо полей sum и result появятся аргументы у методов, обеспечивающие необходимую передачу информации. Вот как выглядит этот класс:

///<summary>

///Класс Account1 определяет банковский счет.

///Вариант с аргументами и функциями

///</summary>

public class Account1

{

//закрытые поля класса int debit=0, credit=0;

///<summary>

///Зачисление на счет с проверкой

///</summary>

///<param name="sum">зачисляемая сумма</param> public void putMoney(int sum)

{

int res =1;

if (sum >0)credit += sum; else res = -1; Mes(res,sum);

}//putMoney

///<summary>

///Снятие со счета с проверкой

///</summary>

///<param name="sum"> снимаемая сумма</param> public void getMoney(int sum)

{

int res=2;

if(sum <= balance())debit += sum; else res = -2;

balance(); Mes(res, sum);

}//getMoney

///<summary>

///вычисление баланса

///</summary>

///<returns>текущий баланс</returns> int balance()

{

return(credit - debit);

}

///<summary>

///Уведомление о выполнении операции

///</summary>

void Mes(int result, int sum)

{

switch (result)

{

case 1:

Console.WriteLine("Операция зачисления денег прошла успешно!"); Console.WriteLine("Cумма={0},

Ваш текущий баланс={1}", sum,balance()); break;

case 2:

Console.WriteLine("Операция снятия денег прошла успешно!");

Console.WriteLine("Cумма={0},

Ваш текущий баланс={1}", sum,balance()); break;

case -1:

Console.WriteLine("Операция зачисления денег не выполнена!");

Console.WriteLine("Сумма должна быть больше нуля!"); Console.WriteLine("Cумма={0},

Ваш текущий баланс={1}", sum,balance()); break;

case -2:

Console.WriteLine("Операция снятия денег не выполнена!"); Console.WriteLine("Сумма должна быть

не больше баланса!"); Console.WriteLine("Cумма={0},

Ваш текущий баланс={1}", sum,balance()); break;

default:

Console.WriteLine("Неизвестная операция!"); break;

}

}

}//Account1

Сравнивая этот класс с классом Account, можно видеть, что число полей сократилось с пяти до двух, упростились основные методы getMoney и putMoney. Но, в качестве платы, у класса появился дополнительный метод balance(), многократно вызываемый, и у метода Mes теперь появились два аргумента. Какой класс лучше? Однозначно сказать нельзя, все зависит от контекста, от приоритетов, заданных при создании конкретной системы.

Приведу процедуру класса Testing, тестирующую работу с классами Account и Account1:

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