Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ЛР_Классы.doc
Скачиваний:
12
Добавлен:
31.05.2015
Размер:
189.95 Кб
Скачать

Методы класса Rational

Если поля класса почти всегда закрываются, чтобы скрыть от пользователя представление данных класса, то методы класса всегда имеют открытую часть – те сервисы (службы), которые класс предоставляет своим клиентам и наследникам. Но не все методы открываются. Большая часть методов класса может быть закрытой, скрывая от клиентов детали реализации, необходимые для внутреннего использования. Заметьте, скрытие представления и реализации делается не по соображениям утаивания того, как реализована система. Чаще всего, ничто не мешает клиентам ознакомиться с полным текстом класса. Скрытие делается в интересах самих клиентов. При сопровождении программной системы изменения в ней неизбежны. Клиенты не почувствуют на себе негативные последствия изменений, если они делаются в закрытой части класса. Чем больше закрытая часть класса, тем меньше влияние изменений на клиентов класса.

Закрытый метод НОД

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

/// <summary>

/// Закрытый метод класса.

/// Возвращает наибольший общий делитель чисел a,b

/// </summary>

/// <param name="a">первое число</param>

/// <param name="b">второе число, положительное</param>

/// <returns>НОД(a,b)</returns>

int nod(int m, int n)

{

int p=0;

m=Math.Abs(m); n =Math.Abs(n);

if(n>m){p=m; m=n; n=p;}

do

{

p = m%n; m=n; n=p;

}while (n!=0);

return(m);

}//nod

Печать рациональных чисел

Почти любой класс содержит один или несколько методов, позволяющих выводить на печать данные о классе. Такой метод имеется и в классе Rational. Вот его текст:

public void PrintRational(string name)

{

Console.WriteLine(" {0} = {1}/{2}",name,m,n);

}

Метод печатает имя и значение рационального числа в форме m/n.

Тестирование создания рациональных чисел

В классе Testing, предназначенном для тестирования нашей работы и являющегося клиентом класса Rational, создадим процедуру, позволяющую проверить корректность создания рациональных чисел. Вот эта процедура:

public void TestCreateRational()

{

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);

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)");

}

Она создает и печатает шесть рациональных чисел. Вот как выглядят результаты ее работы:

Рис. 16.3. Создание и печать рациональных чисел

Операции над рациональными числами

Определим над рациональными числами стандартный набор операций – сложение и вычитание, умножение и деление. Реализуем эти операции методами с именами 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 реализуется просто. По правилам сложения дробей вычисляется числитель и знаменатель результата, и эти данные становятся аргументами конструктора, создающего требуемое рациональное число, удовлетворяющее правилам класса.

Обратите внимание на то, как определяется операция класса. Именем соответствующего метода является сам знак операции, которому предшествует ключевое слово operator. Важно также помнить, что операция является статическим методом класса с атрибутом static.

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

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

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

}

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

Рис. 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 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;

r1.PrintRational("r1: (1,2)"); r2.PrintRational("r2: (1,3)");

r3.PrintRational("r3: (r1-r2)"); r4.PrintRational("r4: (r1*r2)");

r5.PrintRational("r5: (r1/r2)"); r6.PrintRational("r6: (r3+r4*r5)");

}

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

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

Константы класса 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

Вариант 1

  1. Класс имеет:

  • только один конструктор;

  • только конструктор по умолчанию;

  • не более одного статического конструктора;

  • не более одного закрытого конструктора;

  • несколько конструкторов с одинаковой сигнатурой.

  1. Отметьте истинные высказывания:

  • статические методы класса могут вызывать любые методы класса;

  • все конструкторы класса вызываются только при выполнении операции new;

  • свойства задаются для закрытых полей класса;

  • каждый объект содержит набор из всех полей, определенных в классе.

  1. Свойство класса:

  • может иметь атрибут доступа private;

  • может включать методы get и set;

  • включает только один из методов get или set;

  • позволяет реализовать различные стратегии доступа к закрытым полям класса;

  • обязательно должно быть указано для каждого закрытого поля класса.

Вариант 2

  1. Объекты класса Rational могут быть созданы в клиентском классе:

  • конструктором по умолчанию;

  • конструктором с аргументами;

  • закрытым конструктором;

  • статическим конструктором.

  1. Отметьте истинные высказывания:

  • любые методы класса могут вызывать статические методы;

  • конструкторы класса вызываются автоматически;

  • у класса может быть только один индексатор

  • класс не может иметь закрытых методов.

  1. Поля класса:

  • могут иметь любой из модификаторов доступа: public, private, protected, internal;

  • могут иметь модификатор static;

  • обычно закрываются;

  • закрытые поля доступны методам класса.

Вариант 3

  1. Тело класса может содержать описание:

  • полей;

  • методов;

  • классов;

  • интерфейсов;

  • пространств имен;

  • событий.

  1. Отметьте истинные высказывания:

  • статическое поле класса доступно в любом методе класса;

  • статический конструктор класса вызывается автоматически;

  • у класса может быть задано несколько свойств;

  • у класса может быть задано несколько индексаторов;

  • у класса может быть определено несколько операций.

  1. Пусть p1, p2, p3 – переменные класса Rational. Отметьте правильно построенные конструкции:

  • if(p1 != p2) p3 = p1+p2;

  • p3 = p1+ p2 +1;

  • p3 =(Rational.One + Rational.One)* p2 +p1;

  • p3 +=Rational.One;

  • p3++.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]