Виртуальные функции в C#
Когда класс желает изменить реализацию деталей виртуального метода, он делает это с помощью ключевого слова override. Если базовый класс желает определить метод, который может бъипь (но не обязательно) переопределен в подклассе, он должен пометить его ключевым словом virtual: Троелсен стр.241-243 Если набрать слово override внутри контекста типа класса (и нажать клавишу пробела), то система автодополнения автоматически отобразит список всех переопределяемых членов родительского класса,
Виртуальная функция определяется внутри базового класса и может быть переопределена в производном классе. Ключевое слово virtual указывает на виртуальную функцию, например:
public virtual void fun ( )
{
// Операторы реализации функции
}
Поскольку виртуальная функция переопределяется в производном классе, то это напоминает перегрузку функций. Различие заключается в том, что при перегрузке функций различны списки параметров, а в виртуальных функциях количество и типы аргументов должны строго совпадать, различны только алгоритмы.
Ниже приведена программа, состоящая из базового и двух производных классов. В задаче определяется сопротивление цепи, состоящей из двух резисторов. Резисторы могут быть соединены последовательно или параллельно. При последовательном соединении сопротивления суммируются, при параллельном соединении суммируются проводимости:
Разобраться R → res
Для двух резисторов общее сопротивление равно произведению исходных сопротивлений, которое делится на их сумму.
class Res // Базовый класс
{
protected double res1, res2; // Сопротивления
public Res() { res1 = res2 = 0.0; } // Конструктор без параметров
public virtual double get_chain_resist()
{
return 0.0; // Эта функция должна что-нибудь возвращать
}
// Свойство для первого резистора
public double R1
{
get { return res1; }
set { res1 = value; }
}
// Свойство для второго резистора
public double R2
{
get { return res2; }
set { res2 = value; }
}
}
// Этот класс определяет сопротивление цепи при параллельном
// соединении резисторов */
class Paral : Res
{
// Конструктор принимает 2 параметра и передает их в конструктор
// с параметрами базового класса
public Paral(double r1, double r2): base()
{
this.res1 = r1;
this.res2 = r2;
}
public override double get_chain_resist()
{
return R1*R2/(R1 + R2);
}
}
// Этот класс определяет сопротивление цепи при
// последовательном соединении резисторов
class Sequent : Res
{
// Конструктор c двумя параметрами и наследник пустого
// конструктора базового класса
public Sequent(double r1, double r2) : base()//
{
this.res1 = r1;
this.res2 = r2;
}
public override double get_chain_resist()
{
return R1 + R2; // Итоговое сопротивление
}
}
class Program
{
static void Main(string[] args)
{
double inr1, inr2;
Console.WriteLine("
Введите значения сопротивлений
");
inr1 = double.Parse(Console.ReadLine());
inr2 = double.Parse(Console.ReadLine());
//Объявление базового класса
Res r = new Res ();
// Объявление производного класса для последовательного соединения
r = new Sequent(inr1, inr2);
Console.WriteLine(" R при последовательном соединении = " +
r.get_chain_resist());
// Объявление производного класса для параллельного соединения
r = new Paral(inr1, inr2);
Console.WriteLine(" R при параллельном соединении = " +
r.get_chain_resist());
Console.ReadKey(); // Для останова экрана пользователя
}
}
Пример выполнения программы:
В приведенном выше примере виртуальная функция базового класса не имеет выполняемых действий. Полезные операции она выполняет только после переопределения в производных классах. Это распространенная ситуация, при которой базовый класс содержит только набор пустых функций, которые должны переопределяться в производных классах.
У одного базового класса может быть несколько производных классов. В каждом из таких классов виртуальная функция, указанная в базовом классе, может переопределяться по-разному. Ссылка на базовый класс можно использовать для обращения к членам производных классов, а это позволяет иметь одинаковый интерфейс для выполнения разных действий с объектом. Этот принцип называется полиморфизмом.
Для иллюстрации сказанного в приведенной выше программе для получения значений сопротивлений для разных видов соединения применяется одна и та же форма вызова:
r.get_chain_resist(), где r объект базового класса.
Если виртуальная функция в базовом классе не выполняет никаких действий, ее можно объявить абстрактной, чтобы подчеркнуть, что она пустая. Для приведенного выше примера объявление будет иметь вид:
public abstract double get_chain_resist();
Если класс имеет хотя бы одну виртуальную функцию, то он называется абстрактным.
Ниже приведена программа из книги Г. Шилдта «Полный справочник по языку C#», в которой рассматривается несколько производных классов. Для этой программе выполнена косметическая правка
class TwoDShape
{
double pri_width;
double pri_height;
public TwoDShape() // Конструктор по умолчанию,
{
Width = Height = 0.0; // параметры конструктора определены в свойствах
name = "null";
}
// Конструктор с параметрами.
public TwoDShape(double w, double h, string n)
{
Width = w;
Height = h; name = n;
}
// Конструктор объекта с одинаковой шириной и высотой.
public TwoDShape(double x, string n)
{
Width = Height = x;
name = n;
}
// Конструктор копирования для объекта TwoDShape.
public TwoDShape(TwoDShape ob)
{
Width = ob.Width;
Height = ob.Height;
name = ob.name;
}
// Свойства ширины и высоты объекта,
public double Width
{
get { return pri_width; }
set { pri_width = value < 0 ? -value : value; }
}
public double Height
{
get { return pri_height; }
set { pri_height = value < 0 ? -value : value; }
}
public string name { get; set; } // Свойство для названия
public void ShowDim()
{
Console.WriteLine("Ширина и высота равны "+Width + " и " + Height);
}
public virtual double Area()
{
return 0.0;
}
}
// Класс для треугольников, производный от класса TwoDShape.
class Triangle : Two Shape
{
string Style;
public Triangle() // Конструктор, используемый по умолчанию,
{
Style = "null";
}
// Конструктор для класса Triangle,
public Triangle(string s, double w, double h) : base(w, h, "треугольник")
{
Style = s;
}
// Конструктор для равнобедренного треугольника,
public Triangle(double x) : base(x, "треугольник")
{
Style = "равнобедренный";
}
// Конструктор копирования для объекта типа Triangle,
public Triangle(Triangle ob) : base(ob)
{
Style = ob.Style;
}
// Переопределить метод Area() для класса Triangle,
public override double Area() { return Width * Height / 2; }
public void ShowStyle() // Показать тип треугольника
{
Console.WriteLine("Треугольник " + Style);
}
}
//Класс для прямоугольников, производный от класса TwoDShape.
class Rectangle : TwoDShape
{
// Конструктор для класса Rectangle,
public Rectangle(double w, double h) : base(w, h, "прямоугольник") { }
// Конструктор для квадрата
public Rectangle(double x) : base(x, "прямоугольник") { }
// Конструктор копирования объекта типа Rectangle,
public Rectangle(Rectangle ob) : base(ob) { }
// Возвратить логическое значение true, если квадрат.
public bool IsSquare()
{
if (Width == Height) return true;
return false;
}
// Переопределить метод Area() для класса Rectangle,
public override double Area()
{
return Width * Height;
}
}
class DynShapes
{
static void Main()
{
TwoDShape[] shapes = new TwoDShape[5];
shapes[0] = new Triangle("прямоугольный", 8.0, 12.0);
shapes[1] = new Rectangle(10);
shapes[2] = new Rectangle(10, 4);
shapes[3] = new Triangle(7.0);
shapes[4] = new TwoDShape(10, 20, "общая форма");
for (int i = 0; i < shapes.Length; i++)
{
Console.WriteLine("Объект — " + shapes[i].name);
Console.WriteLine("Площадь равна " + shapes[i].Area());
Console.WriteLine();
}
Console.ReadKey();
}
}
Обратите внимание на вызов разных конструкторов. Ниже приведен пример вывода.
Виртуальные функции в C#
Лекции по предмету «Программирование»