Добавить в цитаты Настройки чтения

Страница 309 из 371



public struct Rational

{

     public Rational(int a, int b)

     {

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

         else

         {

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

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

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

              int р = 1, m1=a, n1 =b;

              m1=Math.Abs(m1); nl =Math.Abs(n1);

              if(n1>ml){p=m1; m1=n1; n1=p;}

              do

              {

                   p = m1%n1; m1=n1; n1=p;

               }while (n1!=0);

               p=m1;

               m=a/p; n=b/p;

          }

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

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

}

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

public void TwoSemantics()

{

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

    Rational r3, r4;

    r3 = r1+r2; r4 = r3;

    if(r3 >1) r3 = r1+r3 + Rational.One;

    else r3 = r2+r3 — Rational.One;

    r3.PrintRational("r3"); r4.PrintRational("r4");

}

В этом примере используются константы, работает статический конструктор, закрытый конструктор, перегруженные операции сравнения, арифметические выражения над рациональными числами. В результате вычислений r3 получит значение 8/15, r4=14/15. Заметьте, аналогичный пример для класса Rational даст те же результаты. Для класса Rational и структуры Rational нельзя обнаружить разницу между ссылочным и развернутым присваиванием. Это связано с особенностью класса Rational — он по построению относится к неизменяемым (immutable) классам, аналогично классу string. Операции этого класса не изменяют поля объекта, а каждый раз создают новый объект. В этом случае можно считать, что объекты класса обладают присваиванием развернутого типа.

Встроенные структуры

Как уже говорилось, все значимые типы языка реализованы структурами. В библиотеке FCL имеются и другие встроенные структуры. Рассмотрим в качестве примера структуры Point, PointF, Size, SizeF и Rectangle, находящиеся в пространстве имен System.Drawing и активно используемые при работе с графическими объектами. Первые четыре структуры имеют два открытых поля х и y (Height и Width), задающие для точек — структур Point и PointF — координаты, целочисленные или в форме с плавающей точкой. Для размеров — структур Size и SizeF — они задают высоту и ширину, целочисленными значениями или в форме с плавающей точкой. Структуры Point и Size позволяют задать прямоугольную область — структуру Rectangle. Конструктору прямоугольника можно передать в качестве аргументов две структуры — точку, задающую координаты левого верхнего угла прямоугольника, и размер — высоту и ширину прямоугольника.

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

public void TestPointAndSize()

{

    Point pt1 = new Point(3,5), pt2 = new Point(7,10), pt3;

    PointF pt4 = new PointF(4.55f,6.75f);

    Size sz1 = new Size(10,20), sz2;

    SizeF sz3 = new SizeF(10.3f, 20.7f);

    pt3 = Point.Round(pt4);

    sz2 = new Size (pt1);





    Console.WriteLine ("pt1: " + pt1);

    Console.WriteLine ("sz2 =new Size (pt1): " + sz2);

    Console.WriteLine ("pt4: " + pt4);

    Console.WriteLine("pt3 =Point.Round(pt4): " + pt3);

    pt1.Offset (5,7);

    Console.WriteLine ("pt1.Offset(5,7): + pt1);

    Console.WriteLine ("pt2: " + pt2);

    pt2 = pt2 + sz2;

    Console.WriteLine ("pt2= pt2 + sz2: " + pt2)

}//TestPointAndSize

Результаты его выполнения показаны на рис. 17.1

Рис. 17.1. Операции над точками и размерами

Отметим, что метод Tostring, определенный для этих структур, выдает строку со значениями полей в приемлемой для восприятия форме.

Еще раз о двух семантиках присваивания

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

public void TestTwoSemantics()

{

    Console.WriteLine("Структуры: присваивание развернутого типа!");

    Point pt1 = new Point(3,5), pt2;

    pt2 = pt1;

    Console.WriteLine ("pt1: " + pt1);

    Console.WriteLine ("pt2=pt1: " + pt2);

    pt1.X +=10;

    Console.WriteLine ("pt1.X =pt1.X +10: " + pt1);

    Console.WriteLine ("pt2: " + pt2);

    Console.WriteLine("Классы: присваивание ссылочного типа!");

    CPoint cpt1 = new CPoint(3,5), cpt2; cpt2 = cpt1;

    Console.WriteLine ("cpt1: " + cpt1);

    Console.WriteLine ("cpt2=cpt1: " + cpt2);

    cpt1.X +=10;

    Console.WriteLine ("cptl.X =cpt1.X +10: " + cpt1);

    Console.WriteLine ("cpt2: " + cpt2);

}

Результаты вычислений показаны на рис. 17.2.

Рис. 17.2. Две семантики присваивания

Действия над объектами Point и CPoint выполняются аналогичные а результаты получаются разные: в конце вычислений pt1 и pt2 различны, a cpt1 и cpt2 совпадают.

Перечисления

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

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

{список_возможных_значений}

Описание атрибутов отложим на последующие лекции. Модификаторами могут быть четыре известных модификатора доступа и модификатор new. Ключевое слово enum говорит, что определяется частный случай класса — перечисление. Список возможных значений задает те значения, которые могут получать объекты этого класса. Возможные значения должны быть идентификаторами; но допускаются в их написании и буквы русского алфавита. Можно указать также базовый для перечисления класс.

Дело в том, что значения, заданные списком, проецируются на плотное подмножество базового класса. Реально значения объектов перечисления в памяти задаются значениями базового класса, так же, как значения класса bool реально представлены в памяти нулем и единицей, а не константами true и false, удобными для их использования программистами в тексте программ. По умолчанию, базовым классом является класс int, а подмножество проекции начинается с нуля. Но при желании можно изменить интервал представления и сам базовый класс. Естественно, на базовый класс накладывается ограничение. Он должен быть одним из встроенных классов, задающих счетное множество (int, byte, long, другие счетные типы). Единственное исключение из этого правила — нельзя выбирать класс char в качестве базового класса. Как правило, принятый по умолчанию выбор базового класса и его подмножества вполне приемлем в большинстве ситуаций.