Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Другие элементы меню 13 страница




string fam="", status="", health="";

int age=0, salary=0;

//методы - свойства

/// <summary>

///стратегия: Read,Write-once (Чтение, запись при первом обращении)

/// </summary>

public string Fam

{

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

get { return (fam);}

}

/// <summary>

///стратегия: Read-only(Только чтение)

/// </summary>

public string Status

{

get { return (status);}

}

/// <summary>

///стратегия: Read,Write (Чтение, запись)

/// </summary>

public int Age

{

{

age = value;

if (age < 7)status ="ребенок";

else if (age <17)status ="школьник";

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

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

}

get { return (age);}

}

/// <summary>

///стратегия: Write-only (Только запись)

/// </summary>

public int Salary

{

set {salary = value;}

}

}

Рассмотрим теперь общий синтаксис методов-свойств. Пусть name – это закрытое свойство. Тогда для него можно определить открытый метод-свойство (функцию), возвращающую тот же тип, что и поле name. Имя метода обычно близко к имени поля (например, Name). Тело свойства содержит два метода – get и set, один из которых может быть опущен. Метод get возвращает значение закрытого поля, метод set – устанавливает значение, используя передаваемое ему значение в момент вызова, хранящееся в служебной переменной со стандартным именем value. Поскольку get и set – это обычные процедуры языка, то программно можно реализовать сколь угодно сложные стратегии доступа. В нашем примере фамилия меняется, только если ее значение равно пустой строке, означающее, что фамилия персоны ни разу еще не задавалась. Статус персоны пересчитывается автоматически при всяком изменении возраста, явно изменять его нельзя. Вот пример, показывающий, как некоторый клиент создает и работает с полями персоны:

public void TestPersonProps()

{

Person pers1 = new Person();

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

pers1.Age = 21;

pers1.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);

}// TestPersonProps

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

Рис. 16.1. Методы-свойства и стратегии доступа к полям

Индексаторы

Свойства являются частным случаем метода класса с особым синтаксисом. Еще одним частным случаем является индексатор. Метод-индексатор является обобщением метода-свойство. Он обеспечивает доступ к закрытому полю, представляющему массив. Объекты класса индексируются по этому полю.

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

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

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

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++;}

}

}

Имя у индексатора this, в квадратных скобках в заголовке перечисляются индексы. В методах get, set, обеспечивающих доступ к массиву children, по которому ведется индексирование, анализируется корректность задания индекса. Закрытое поле count_children, хранящее текущее число детей, доступно только для чтения добавлением соответствующего метода-свойства. Запись в это поле происходит в методе set индексатора, когда к массиву 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;

Person pers3= new Person("Петрова");

pers1[pers1.Count_children] = pers3;

pers3.Fam ="Петрова"; pers3.Age = 5;

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);

Console.WriteLine ("Дочь={0}, возраст={1}, статус={2}",

pers1[1].Fam, pers1[1].Age, pers1[1].Status);

}

Заметьте, индексатор создает из объекта как бы массив объектов, индексированный по соответствующему полю, в данном случае по полю children. На рис. 16.2 показаны результаты вывода:

Рис. 16.2. Работа с индексатором класса

Операции

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

Статические поля и методы класса

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

Аналогично полям у класса могут быть и статические методы, объявленные с модификатором static. Такие методы не используют информацию о свойствах конкретных объектов класса, они обрабатывают общую для класса информацию, хранящуюся в статических полях класса. Например в классе Person может быть статический метод, обрабатывающий данные из статического поля message. Другим частым случаем применения статических методов является ситуация, когда класс предоставляет свои сервисы объектам других классов. Таковым является класс Math из библиотеки FCL, который не имеет собственных полей, все его статические методы работают с объектами арифметических классов.

Как вызываются статические поля и методы? Напомню, что всякий вызов метода в объектных вычислениях имеет вид x.F(…); где x – это цель вызова. Обычно целью вызова является объект, вызывающий методы классы, не являющиеся статическими (динамическими или экземплярными). В этом случае поля целевого объекта доступны методу и служат глобальным источником информации. Если же необходимо вызвать статический метод (поле), то целью должен быть сам класс. Можно полагать, что для каждого класса автоматически создается статический объект с именем класса, содержащий статические поля и обладающий статическими методами. Этот объект и его методы доступны и тогда, когда ни один другой динамический объект класса еще не создан.

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

Константы

В классе могут быть объявлены константы. Синтаксис объявления приводился в лекции 5. Константы фактически являются статическими полями доступными только для чтения, значения которых задаются при инициализации. Однако задавать модификатор static для констант не только не нужно, но и запрещено. В нашем классе Person была задана константа Child_Max, характеризующая максимальное число детей у персоны.

Никаких проблем с константами не возникает, когда речь идет о константах встроенных типов, таких как Child_Max. Однако совсем не просто определить в языке C# константы собственного класса. Этот вопрос будет подробно рассматриваться чуть позже, когда речь пойдет о проектировании класса Rational.

Конструкторы класса

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

Как и когда происходит создание объектов? Чаще всего, при объявлении сущности в момент ее инициализации. Давайте обратимся к нашему последнему примеру и рассмотрим создание трех объектов класса Person:

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

Person pers3= new Person("Петрова");

Сущности pers1, pers2, pers3 класса Person объявляются с инициализацией, задаваемой унарной операцией new, которой в качестве аргумента передается конструктор класса Person. У класса может быть несколько конструкторов – это типичная практика, – отличающихся сигнатурой. В данном примере в первой строке вызывается конструктор без аргументов, во второй строке для сущности pers3 вызывается конструктор с одним аргументом типа string. Разберем в деталях процесс создания:

Первым делом для сущности pers создается ссылка, пока висячая со значением null.

Затем в динамической памяти создается объект – структура данных с полями, определяемыми классом Person. Поля объекта инициализируются значениями по умолчанию: ссылочные поля – значением null, арифметические – нулями, строковые – пустой строкой. Эту работу выполняет конструктор по умолчанию, который можно считать всегда вызывается в начале процесса создания. Заметьте, если инициализируется переменная значимого типа, то все происходит аналогичным образом, за исключением того, что объект создается в стеке.

Если поля класса проинициализированы, как в нашем примере, то выполняется инициализация полей заданными значениями.

Если вызван конструктор с аргументами, то начинает выполняться тело этого конструктора. Как правило при этом происходит инициализация отдельных полей класса значениями, переданными конструктору. Так поле fam объекта pers3 получает значение «Петрова».

На заключительном этапе ссылка связывается с созданным объектом.

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

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

Зачем классу нужно несколько конструкторов? Дело в том, что в зависимости от контекста и создаваемого объекта может требоваться различная инициализация его полей. Перегрузка конструкторов и обеспечивает решение этой задачи.

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

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

static Person()

{

Console.WriteLine("Выполняется статический конструктор!");

}

В нашей тестирующей процедуре, работающей с объектами класса Person, этот конструктор вызывается первым и первым появляется сообщение этого конструктора.

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

Деструкторы класса

Если задача создания объектов полностью возлагается на программиста, то задача удаления объектов, после того, как они стали не нужными, в Visual Studio.Net снята с программиста и возложена на соответствующий инструментарий – сборщик мусора. В классическом варианте языка C++ деструктор также необходим классу, как и конструктор. В языке C# y класса может быть деструктор, но он не занимается удалением объектов и не вызывается нормальным образом в ходе выполнения программы. Также как и статический конструктор, деструктор класса, если он есть, вызывается автоматически в процессе сборки мусора. Его роль в освобождении ресурсов, например файлов, открытых объектом. Деструктор C# фактически является финализатором (finalizer), с которыми мы еще встретимся при обсуждении исключительных ситуаций. Приведу формальное описание деструктора класса Person:

~Person()

{

//Код деструктора

}

Имя деструктора строится из имени класса с предшествующим ему символом ~ (тильда). Как и у статического конструктора, у деструктора не указывается модификатор доступа.

Проектирование класса Rational

В заключение этой лекции займемся проектированием класса Rational, описывающего известный в математике тип данных – рациональные числа. По ходу проектирования будут вводиться новые детали, связанные с описанием класса. Начнем проектирование, как обычно, с задания тэга summary, описывающего назначение класса, его свойства и поведение. Вот этот текст:

/// <summary>

/// Класс Rational.

/// определяет новый тип данных - рациональные числа и основные

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

/// Рациональное число задается парой целых чисел (m,n) и изображается,

/// обычно в виде дроби m/n. Число m называется числителем,

/// n - знаменателем. Для каждого рационального числа существует

/// множество его представлений, например 1/2, 2/4, 3/6, 6/12 -

/// задают одно и тоже рациональное число. Среди всех представлений

/// можно выделить то, в котором числитель и знаменатель взаимно

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

/// Операции над рациональными числами определяются естественным

/// для математики образом.

/// </summary>

public class Rational

{

// Описание тела класса Rational

}//Rational

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

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

//Поля класса. Числитель и знаменатель рационального числа.

int m,n;

Конструкторы класса Rational

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

/// <summary>

/// Конструктор класса. Создает рациональное число

/// m/n эквивалентное a/b, но со взаимно несократимыми

/// числителем и знаменателем. Если b=0, то результатом

/// является рациональное число 0 -пара (0,1).

/// </summary>

/// <param name="a"> числитель </param>

/// <param name="b"> знаменатель </param>

public Rational(int a, int b)

{

if (b==0) {m=0; n=1;}

{

//приведение знака

if (b<0) {b=-b; a=-a;}

//приведение к несократимой дроби

int d = nod(a,b);

m=a/d; n=b/d;

}

}

Как видите, конструктор класса может быть довольно сложным. В нем, как в нашем случае, может проверяться корректность задаваемых аргументов. Для рациональных чисел мы полагаем, что задание нулевого знаменателя означает задание рационального числа 0, что эквивалентно заданию пары (0, 1). В остальных случаях выполняется приведение заданной пары чисел к эквивалентному рациональному числу с несократимыми числителем и знаменателем. По ходу дела вызывается закрытый метод класса, вычисляющий значение НОД(a,b) – наибольшего общего делителя чисел a и b.

Методы класса Rational

Если поля класса почти всегда закрываются, чтобы скрыть от пользователя представление данных класса, то методы класса всегда имеют открытую часть – те сервисы (службы), которые класс предоставляет своим клиентам и наследникам. Но не все методы открываются. Большая часть методов класса может быть закрытой, скрывая от клиентов детали реализации, необходимые для внутреннего использования. Заметьте, скрытие представления и реализации делается не по соображениям утаивания того, как реализована система. Чаще всего, ничто не мешает клиентам ознакомиться с полным текстом класса. Скрытие делается в интересах самих клиентов. При сопровождении программной системы изменения в ней неизбежны. Клиенты не почувствуют на себе негативные последствия изменений, если они делаются в закрытой части класса. Чем больше закрытая часть класса, тем меньше влияние изменений на клиентов класса.

Закрытый метод НОД

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

/// <summary>

/// Закрытый метод класса.

/// Возвращает наибольший общий делитель чисел a,b

/// </summary>

/// <param name="a"> первое число </param>

/// <param name="b"> второе число, положительное </param>

/// <returns> НОД(a,b)</returns>

int nod(int m, int n)

{

int p=0;

m=Math.Abs(m); n =Math.Abs(n);

if (n>m){p=m; m=n; n=p;}

{

p = m%n; m=n; n=p;

} while (n!=0);

return (m);

}// nod

Печать рациональных чисел

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

public void PrintRational(string name)

{

Console.WriteLine(" {0} = {1}/{2}",name,m,n);

}

Метод печатает имя и значение рационального числа в форме m/n.

Тестирование создания рациональных чисел

В классе Testing, предназначенном для тестирования нашей работы и являющегося клиентом класса Rational, создадим процедуру, позволяющую проверить корректность создания рациональных чисел. Вот эта процедура:

public void TestCreateRational()

{

Rational r1= new Rational(0,0), r2 = new Rational(1,1);

Rational r3= new Rational(10,8), r4 = new Rational(2,6);

Rational r5= new Rational(4,-12), r6 = new Rational(-12,-14);

r1.PrintRational("r1:(0,0)"); r2.PrintRational("r2:(1,1)");

r3.PrintRational("r3:(10,8)"); r4.PrintRational("r4:(2,6)");

r5.PrintRational("r5: (4,-12)"); r6.PrintRational("r6: (-12,-14)");

}

Она создает и печатает шесть рациональных чисел. Вот как выглядят результаты ее работы:

Рис. 16.3. Создание и печать рациональных чисел

Операции над рациональными числами

Определим над рациональными числами стандартный набор операций – сложение и вычитание, умножение и деление. Реализуем эти операции методами с именами Plus, Minus, Mult, Divide. Поскольку рациональные числа – это, прежде всего числа, то для выполнения операций над ними часто удобнее пользоваться привычными знаками операций (+, –, *, /). Язык C# допускает определение операций, заданных указанными символами. Этот процесс называется перегрузкой операций, и мы рассмотрим сейчас, как это делается. Конечно, можно было бы обойтись только перегруженными операциями, но мы приведем оба способа. Пользователь сам будет решать, какой из способов применять в конкретной ситуации – вызывать метод или операцию.

Покажем вначале реализацию метода Plus и операции +:

public Rational Plus(Rational a)

{

int u,v;

u = m*a.n +n*a.m; v= n*a.n;

return (new Rational(u, v));

}// Plus

public static Rational operator +(Rational r1, Rational r2)

{

return (r1.Plus(r2));

}

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

Обратите внимание на то, как определяется операция класса. Именем соответствующего метода является сам знак операции, которому предшествует ключевое слово operator. Важно также помнить, что операция является статическим методом класса с атрибутом static.

В данном конкретном случае операция реализуется вызовом метода Plus. Как теперь все это работает? Вот пример:

public void TestPlusRational()

{

Rational r1= new Rational(0,0), r2 = new Rational(1,1);

Rational r3= new Rational(10,8), r4 = new Rational(2,6);

Rational r5= new Rational(4,-12), r6 = new Rational(-12,-14);

Rational r7,r8, r9,r10, r11, r12;

r7 = r1.Plus(r2); r8 = r3.Plus(r4); r9 = r5.Plus(r6);

r10 = r1+r2; r11 = r3+r4; r12 = r5+r6+r10+r11;

r1.PrintRational("r1:(0,0)"); r2.PrintRational("r2:(1,1)");

r3.PrintRational("r3:(10,8)"); r4.PrintRational("r4:(2,6)");

r5.PrintRational("r5: (4,-12)"); r6.PrintRational("r6: (-12,-14)");

r7.PrintRational("r7: (r1+r2)"); r8.PrintRational("r8: (r3+r4)");

r9.PrintRational("r9: (r5+r6)"); r10.PrintRational("r10: (r1+r2)");

r11.PrintRational("r11: (r3+r4)");

r12.PrintRational("r12: (r5+r6+r10+r11)");

}

Обратите внимание на вычисление r12, здесь ощутимо видно преимущество операций, позволяющих записывать сложные выражения в простой форме. Результаты вычислений показаны на рис. 16.4.

Рис. 16.4. Сложение рациональных чисел

Аналогичным образом определим остальные операции над рациональными числами:

public Rational Minus(Rational a)

{

int u,v;

u = m*a.n - n*a.m; v= n*a.n;

return (new Rational(u, v));

}// Minus

public static Rational operator -(Rational r1, Rational r2)

{

return (r1.Minus(r2));

}

public Rational Mult(Rational a)

{

int u,v;

u = m*a.m; v= n*a.n;

return (new Rational(u, v));

}// Mult

public static Rational operator *(Rational r1, Rational r2)

{

return (r1.Mult(r2));

}

public Rational Divide(Rational a)

{

int u,v;

u = m*a.n; v= n*a.m;

return (new Rational(u, v));

}// Divide

public static Rational operator /(Rational r1, Rational r2)

{

return (r1.Divide(r2));

}

Вот тест, проверяющий работу этих операций:

public void TestOperRational()

{

Rational r1= new Rational(1,2), r2 = new Rational(1,3);

Rational r3, r4, r5, r6;

r3 = r1- r2; r4 = r1*r2; r5 = r1/r2; r6 = r3+r4*r5;

r1.PrintRational("r1: (1,2)"); r2.PrintRational("r2: (1,3)");

r3.PrintRational("r3: (r1-r2)"); r4.PrintRational("r4: (r1*r2)");

r5.PrintRational("r5: (r1/r2)"); r6.PrintRational("r6: (r3+r4*r5)");

}

Результаты работы этого теста показаны на рис. 16.5. Обратите внимание, при перегрузке операций сохраняется общепринятый приоритет операций. Поэтому при вычислении выражения r3+r4*r5 вначале будет выполняться умножение рациональных чисел, а потом уже сложение.

Рис. 16.5. Операции и выражения над рациональными числами

Константы класса Rational

Рассмотрим важную проблему определения констант в собственном классе. Определим две константы 0 и 1 класса Rational. Кажется, что сделать это невозможно из-за ограничений, накладываемых на объявление констант. Напомню, константы должны быть инициализированы в момент объявления и их значения должны быть заданы константными выражениями, известными в момент компиляции. Но в момент компиляции у класса Rational нет никаких известных константных выражений. Как же быть? Справиться с этой задачей поможет статический конструктор, созданный для решения подобных задач. Роль констант класса будут играть статические поля, объявленные с атрибутом readonly, доступные только для чтения. Нам также будет полезен закрытый конструктор класса. Также что введение констант класса требует использования экзотических средств языка C#. Вначале определим закрытый конструктор:




Поделиться с друзьями:


Дата добавления: 2014-12-25; Просмотров: 384; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.148 сек.