📚 Hub Books: Онлайн-чтение книгРазная литератураИнтернет-журнал "Домашняя лаборатория", 2007 №9 - Журнал «Домашняя лаборатория»

Интернет-журнал "Домашняя лаборатория", 2007 №9 - Журнал «Домашняя лаборатория»

Шрифт:

-
+

Интервал:

-
+
1 ... 348 349 350 351 352 353 354 355 356 ... 415
Перейти на страницу:
говорил, задаются статическими полями с атрибутом readonly:

//Константы класса 0 и 1 — Zero и One

       public static readonly Rational Zero, One;

А теперь зададим статический конструктор, в котором определяются значения констант:

static Rational()

{

     Console.WriteLine("static constructor Rational");

     Zero = new Rational(0, 1, "private");

     One = new Rational (1, 1, "private");

}//Статический конструктор

Как это все работает? Статический конструктор вызывается автоматически один раз до начала работы с объектами класса. Он и задаст значения статических полей Zero, One, представляющих рациональные числа с заданным значением. Поскольку эти поля имеют атрибут static и readonly, то они доступны для всех объектов класса и не изменяются в ходе вычислений, являясь настоящими константами класса. Прежде чем привести пример работы с константами, давайте добавим в наш класс важные булевы операции над рациональными числами — равенство и неравенство, больше и меньше. При этом две последние операции сделаем перегруженными, позволяя сравнивать рациональные числа с числами типа double:

public static bool operator ==(Rational r1, Rational r2)

}

     return ((r1.m==r2.m) & & (r1.n==r2.n));

}

     public static bool operator!=(Rational r1, Rational r2)

}

     return ((r1.m! =r2.m) || (r1.n!=r2.n));

}

     public static bool operator <(Rational r1, Rational r2)

}

     return (r1.m*r2. n < r2.m* r1.n);

}

     public static bool operator >(Rational r1, Rational r2)

}

     return (r1.m * r2.n > r2.m* r1.n);

}

     public static bool operator <(Rational rl, double r2)

{

     return((double)r1.m / (double)rl.n < r2);

}

     public static bool operator >(Rational rl, double r2)

{

     return((double)r1.m / (double)r1.n > r2);

}

Наш последний пример демонстрирует работу с константами, булевыми и арифметическими выражениями над рациональными числами:

public void TestRationalConst()

{

     Rational r1 = new Rational(2,8), r2 =new Rational(2,5);

     Rational r3 = new Rational(4, 10), r4 = new Rational(3,7);

     Rational r5 = Rational.Zero, r6 = Rational.Zero;

     if ((r1!= Rational.Zero) && (r2 == r3)) r5 =(r3+Rational.One)*r4;

     r6 = Rational.One + Rational.One;

     r1.PrintRational ("r1: (2,8)");

     r2.PrintRational ("r2: (2,5)");

     r3.PrintRational ("r3: (4,10)");

     r4.PrintRational ("r4: (3,7)");

     r5.PrintRational ("r5: ((r3 +1)*r4)");

     r6.PrintRational ("r6: (1 + 1)");

}

Результаты работы этого примера показаны на рис. 16.6.

Рис. 16.6. Константы и выражения типа Rational

17. Структуры и перечисления

Понятие развернутого и ссылочного типа. Структуры — реализация развернутых классов. Синтаксис структур. Сравнение структур и классов. Встроенные структуры. Перечисление — частный случай класса. Особенности перечислений. Примеры.

Развернутые и ссылочные типы

Рассмотрим объявление объекта класса T с инициализацией:

Т х = new Т();

Напомню, как выполняется этот оператор. В памяти создается объект типа T, основанного на классе T, и сущность х связывается с этим объектом. Сущность, не прошедшая инициализацию (явную или неявную), не связана ни с одним объектом, а потому не может использоваться в вычислениях — у нее нет полей, хранящих значения, она не может вызывать методы класса. Объектам нужна память, чтобы с ними можно было работать. Есть две классические стратегии выделения памяти и связывания объекта, создаваемого в памяти, и сущности, объявленной в тексте.

Определение 1. Класс T относится к развернутому типу, если память отводится сущности х; объект разворачивается на памяти, жестко связанной с сущностью.

Определение 2. Класс T относится к ссылочному типу, если память отводится объекту; сущность х является ссылкой на объект.

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

Развернутые и ссылочные типы порождают две различные семантики присваивания — развернутое присваивание и ссылочное присваивание. Рассмотрим присваивание:

у = х;

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

Язык программирования должен позволять программисту в момент определения класса указать, к развернутому или ссылочному типу относится класс. К сожалению, язык C# не позволяет этого сделать напрямую — в нем у класса нет модификатора, позволяющего задать развернутый или ссылочный тип. Какие же средства языка позволяют частично решить эту важную задачу? В лекции 3, где рассматривалась система типов языка С#, отмечалось, что все типы языка делятся на ссылочные и значимые. Термин "значимый" является синонимом термина "развернутый". Беда только в том, что деление на значимые и ссылочные типы предопределено языком и не управляется программистом. Напомню, к значимым типам относятся все встроенные арифметические типы, булев тип, структуры, к ссылочным типам — массивы, строки, классы. Так можно ли в C# спроектировать свой собственный класс так, чтобы он относился к значимым типам? Ответ на это вопрос положительный, хотя и с рядом оговорок. Для того чтобы класс отнести к значимым типам, его нужно реализовать как структуру.

Классы и структуры

Структура — это частный случай класса. Исторически структуры используются в языках программирования раньше классов. В языках PL/1, С и Pascal они представляли собой только совокупность данных (полей класса), но не включали ни методов, ни событий. В языке C++ возможности структур были существенно расширены и они стали настоящими классами, хотя и с некоторыми ограничениями. В языке C# — наследнике C++ — сохранен именно такой подход к структурам.

Чем следует руководствоваться, делая выбор между структурой и классом? Полагаю, можно пользоваться следующими правилами:

• если необходимо отнести класс к развернутому типу, делайте его структурой;

• если у класса число полей относительно невелико, а число возможных объектов

1 ... 348 349 350 351 352 353 354 355 356 ... 415
Перейти на страницу:

Комментарии

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

Никто еще не прокомментировал. Хотите быть первым, кто выскажется?