Константы класса Rational
Рассмотрим важную проблему определения констант в собственном классе. Определим две константы 0 и 1 класса Rational. Кажется, что сделать это невозможно из-за ограничений, накладываемых на объявление констант. Напомню, константы должны быть инициализированы в момент объявления, и их значения должны быть заданы константными выражениями, известными в момент компиляции. Но в момент компиляции у класса Rational нет никаких известных константных выражений. Как же быть? Справиться с проблемой поможет статический конструктор, созданный для решения подобных задач. Роль констант класса будут играть статические поля, объявленные с атрибутом readonly, то есть доступные только для чтения. Нам также будет полезен закрытый конструктор класса. Еще укажем, что введение констант класса требует использования экзотических средств языка C#. Вначале определим закрытый конструктор:
private Rational(int a, int b, string t) { m = a; n = b; }
Не забудем, что при перегрузке методов (в данном случае конструкторов) сигнатуры должны различаться, и поэтому пришлось ввести дополнительный аргумент 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:
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 r1, double r2) { return((double)r1.m / (double)r1.n < r2); } public static bool operator >(Rational r1, 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