Интернет-журнал "Домашняя лаборатория", 2007 №9
Шрифт:
О полиморфизме говорилось достаточно много в предыдущих лекциях. Тем не менее, позволю напомнить суть дела. Родитель может объявить свой метод виртуальным, в этом случае в контракте на метод потомку разрешается переопределить реализацию, но он не имеет права изменять сигнатуру виртуального метода. Когда некоторый метод родителя Q вызывает виртуальный метод F, то, благодаря позднему связыванию, реализуется полиморфизм и реально будет вызван не метод родителя F, а метод F, который реализован потомком,
Идея примера такова. Вначале построим родительский класс, метод которого будет вычислять интеграл от некоторой подынтегральной функции, заданной виртуальным методом класса. Далее построим класс-потомок, наследующий родительский метод вычисления интеграла и переопределяющий виртуальный метод, в котором потомок задаст собственную подынтегральную функцию. При такой технологии, всякий раз, когда нужно вычислить интеграл, нужно создать класс-потомок, в котором переопределяется виртуальный метод. Приведу пример кода, следующего этой схеме:
class FIntegral
{
//базовый класс, в котором определен метод вычисления
//интеграла и виртуальный метод, задающий базовую
//подынтегральную функцию
public double Evaluatelntegral(double a, double b, double eps)
{
int n=4;
double I0 = 0, I1 = I (a, b, n);
for(n=8; n < Math.Pow(2.0,15.0); n*=2)
{
I0 =I1; I1=1(a,b,n);
if(Math.Abs(I1-I0)<eps)break;
}
if(Math.Abs(I1–10)< eps)
Console.WriteLine("Требуемая точность достигнута! "+
" eps = {0}, достигнутая точность ={1}, n= {2}",
eps,Math.Abs(I1–I0), n);
else
Console.WriteLine("Требуемая точность не достигнута! "+
" eps = {0}, достигнутая точность ={1}, n= {2}",
eps,Math.Abs(I1–I0), n);
return (I1);
}
private double I(double a, double b, int n)
{
//Вычисляет частную сумму по методу трапеций
double х = a, sum = sif(x)/2, dx = (b-a)/п;
for (int i= 2; i <= n; i++)
{
x += dx; sum += sif(x);
}
x = b; sum += sif(x)/2;
return(sum*dx);
}
protected virtual double sif(double x)
{return(1.0);}
}//FIntegral
Этот код большей частью знаком. В отличие от класса HighOrderIntegral, здесь нет делегата, у функции Evaluate integral нет параметра функционального типа. Вместо этого тут же в классе определен защищенный виртуальный метод, задающий конкретную подынтегральную функцию. В качестве таковой выбрана самая простая функция, тождественно равная единице.
Для вычисления интеграла от
class FIntegralSon: FIntegral
{
protected override double sif(double x)
{
double a = 1.0; double b = 2.0; double c= 3.0;
return (double)(a*x*x +b*x +c);
}
}//FIntegralSon
Принципиально задача решена. Осталось только написать фрагмент кода, запускающий вычисления. Он оформлен в виде следующей процедуры:
public void TestPolymorphIntegral
{
FIntegral integral1 = new FIntegral ;
FIntegralSon integral2 = new FIntegralSon;
double res1 = integral1.Evaluatelntegral(2.0,3.0,0.le-5);
double res2 = integral2.Evaluatelntegral(2.0,3.0,0.le-5);
Console.WriteLine("Father = {0}, Son = {1}", resl,res2);
}//PolymorphIntegral
Взгляните на результаты вычислений.
Рис. 20.4. Вычисление интеграла, использующее полиморфизм
Делегаты как свойства
В наших примерах рассматривалась ситуация, при которой в некотором классе объявлялись функции, удовлетворяющие контракту с делегатом, но создание экземпляров делегата и их инициирование функциями класса выполнялось в другом месте, там, где предполагалось вызывать соответствующие функции. Чаще всего, создание экземпляров удобнее возложить на класс, создающий требуемые функции. Более того, в этом классе делегат можно объявить как свойство класса, что позволяет "убить двух зайцев". Во-первых, с пользователей класса снимается забота создания делегатов, что требует некоторой квалификации, которой у пользователя может и не быть. Во-вторых, делегаты создаются динамически, в тот момент, когда они требуются. Это важно как при работе с функциями высших порядков, когда реализаций, например, подынтегральных функций, достаточно много, так и при работе с событиями класса, в основе которых лежат делегаты.
Рассмотрим пример, демонстрирующий и поясняющий эту возможность при работе с функциями высших порядков. Идея примера такова. Спроектируем два класса:
• класс объектов Person с полями: имя, идентификационный номер, зарплата. В этом классе определим различные реализации функции Compare, позволяющие сравнивать два объекта по имени, по номеру, по зарплате, по нескольким полям. Самое интересное, ради чего и строится данный пример: для каждой реализации Compare будет построена процедура-свойство, которая задает реализацию делегата, определенного в классе Persons;
• класс Persons будет играть роль контейнера объектов Person.
В этом классе будут определены операции над объектами. Среди операций нас, прежде всего, будет интересовать сортировка объектов, реализованная в виде функции высших порядков. Функциональный параметр будет задавать класс функций сравнения объектов, реализации которых находятся в классе Person. Делегат, определяющий класс функций сравнения, будет задан в классе Persons.