Здравствуйте! Записался на ваш курс, но не понимаю как произвести оплату. Надо ли писать заявление и, если да, то куда отправлять? как я получу диплом о профессиональной переподготовке? |
Классы
Операции над рациональными числами
Определим над рациональными числами стандартный набор операций - сложение и вычитание, умножение и деление. Реализуем эти операции методами с именами 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 реализуется просто. По правилам сложения дробей вычисляется числитель и знаменатель результата, и эти данные становятся аргументами конструктора, создающего требуемое рациональное число, которое удовлетворяет правилам класса.
Операция является статическим методом класса с модификатором static. Именем соответствующего метода является сам знак операции, которому предшествует ключевое слово operator. Важно понимать, что метод Plus является динамическим, экземплярным методом класса. Перегруженная операция " + " является статическим методом, частью модуля, связанного с классом Rational.
Аналогичным образом определим остальные операции над рациональными числами:
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 - r3)/r4; Console.WriteLine("r1 = " + r1.ToString()); Console.WriteLine("r2 = " + r2.ToString()); Console.WriteLine("r3 = " + r3.ToString()); Console.WriteLine("r4 = " + r4.ToString()); Console.WriteLine("r5 = " + r5.ToString()); Console.WriteLine("r6 = " + r6.ToString()); }
Результаты работы этого теста показаны на рис. 1.2. Обратите внимание: при перегрузке операций и вычислении выражения сохранился общепринятый приоритет операций.
Константы класса Rational
Рассмотрим важную проблему определения констант в собственном классе. Определим две константы 0 и 1 класса Rational. Кажется, что сделать это невозможно из-за ограничений, накладываемых на объявление констант. Напомню, константы должны быть инициализированы в момент объявления и их значения должны быть заданы константными выражениями, известными в момент компиляции. Но в момент компиляции у класса Rational нет никаких известных константных выражений. Как же быть? Справиться с проблемой поможет статический конструктор. Нам также будет полезен закрытый конструктор класса. Роль констант класса будут играть статические поля, объявленные с атрибутом readonly, доступные только для чтения.
//Константы класса 0 и 1 - Zero и One public static readonly Rational Zero, One;
А теперь зададим закрытый и статический конструкторы. Статический конструктор будет определять константы класса. Чтобы создать рациональное число, играющее роль константы, статический конструктор вызывает обычный конструктор. Для этого можно было бы воспользоваться имеющимся открытым конструктором, но давайте для полноты картины определим закрытый конструктор.
Не забудем, что при перегрузке сигнатуры методов должны различаться, и поэтому пришлось в закрытый конструктор ввести дополнительный аргумент t во избежание конфликтов. Поскольку конструктор закрытый, то гарантируется корректное задание аргументов при его вызове:
Rational(int a, int b, string t) { m = a; n = b; }//Закрытый конструктор static Rational() { Zero = new Rational(0, 1, ""); One = new Rational(1, 1, ""); }//Статический конструктор
Как это все работает? Статический конструктор вызывается автоматически один раз до начала работы с объектами класса. Он и задаст значения статических полей Zero, One, представляющих рациональные числа с заданным значением. Поскольку эти поля имеют модификатор public 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 override bool Equals(object obj) { return this == (Rational)obj; } public override int GetHashCode() { return m + n; }
Определив операции сравнения рациональных чисел, логично переопределить и метод Equals, наследуемый от object, а вслед за ним и метод GetHashCode, завершив тем самым определение собственного класса.
Наш последний пример демонстрирует работу с константами, булевыми и арифметическими выражениями над рациональными числами:
public void TestRationalConst() { Rational r1 = new Rational(0, 8), r2 = new Rational(2, 5); Rational r3 = new Rational(4, 10), r4 = new Rational(3, 7); Rational r5 = Rational.Zero, r6, r7; if ((r1 != Rational.Zero) && (r2 == r3)) r6 = (r3 + Rational.One) * r4; else r6 = Rational.One + Rational.One; if ((r6 > r3) && (r4 > 0.5)) r7 = r1; else r7 = r2; Console.WriteLine("r1 = " + r1.ToString()); Console.WriteLine("r2 = " + r2.ToString()); Console.WriteLine("r3 = " + r3.ToString()); Console.WriteLine("r4 = " + r4.ToString()); Console.WriteLine("r5 = " + r5.ToString()); Console.WriteLine("r6 = " + r6.ToString()); Console.WriteLine("r7 = " + r7.ToString()); }
Результаты работы этого примера показаны на рис. 1.3