Выбрать главу

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

В данном конкретном случае операция реализуется вызовом метода 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("9:(r5+r6)"); r10.PrintRational ("r10:(r1+r2)");

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

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

}

Обратите внимание на вычисление r12; здесь ощутимо видно преимущество операций, позволяющих записывать сложные выражения в простой форме. Результаты вычислений показаны на рис. 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 rl, 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)")

     г5.PrintRational("r5: (r1/r2)");

     г6. PrintRational("r6: (r3+r4*r5)");

}

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

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

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

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

private Rational(int a, int b, string t)

{

      m = a; n = b;

}

He забудем, что при перегрузке методов (в данном случае конструкторов) сигнатуры должны различаться, и поэтому пришлось ввести дополнительный аргумент t для избежания конфликтов. Поскольку конструктор закрытый, то гарантируется корректное задание аргументов при его вызове. Определим теперь константы класса, которые, как я уже говорил, задаются статическими полями с атрибутом 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: