Практическое использование виртуальных функций в С#
Какое практическое использование виртуальных функций в С#?
Ответы
Ответ 1
Итак, в основном, если в классе вашего предка вам требуется определенное поведение для метода. Если ваш потомк использует тот же метод, но имеет другую реализацию, вы можете переопределить его, если у него есть ключевое слово virtual.
using System;
class TestClass
{
public class Dimensions
{
public const double pi = Math.PI;
protected double x, y;
public Dimensions()
{
}
public Dimensions (double x, double y)
{
this.x = x;
this.y = y;
}
public virtual double Area()
{
return x*y;
}
}
public class Circle: Dimensions
{
public Circle(double r): base(r, 0)
{
}
public override double Area()
{
return pi * x * x;
}
}
class Sphere: Dimensions
{
public Sphere(double r): base(r, 0)
{
}
public override double Area()
{
return 4 * pi * x * x;
}
}
class Cylinder: Dimensions
{
public Cylinder(double r, double h): base(r, h)
{
}
public override double Area()
{
return 2*pi*x*x + 2*pi*x*y;
}
}
public static void Main()
{
double r = 3.0, h = 5.0;
Dimensions c = new Circle(r);
Dimensions s = new Sphere(r);
Dimensions l = new Cylinder(r, h);
// Display results:
Console.WriteLine("Area of Circle = {0:F2}", c.Area());
Console.WriteLine("Area of Sphere = {0:F2}", s.Area());
Console.WriteLine("Area of Cylinder = {0:F2}", l.Area());
}
}
Изменить: Вопросы в комментарии
Если я не использую ключевое слово virtual в базовом классе, будет ли он работать?
Если вы используете ключевое слово override
в своих классах потомков, это не сработает. Вы будете генерировать ошибку компилятора CS0506 'function1': не может переопределить унаследованный элемент 'function2', потому что он не помечен как "виртуальный", "абстрактный", "переопределение"
Если вы не используете переопределение, вы получите CS0108 warning 'desc.Method()' скрывает базу унаследованных членов. Метод() 'Использовать новое ключевое слово, если было предназначено скрытие.
Чтобы обойти это, поставьте ключевое слово new
перед тем, как вы скрываете.
например.
new public double Area()
{
return 2*pi*x*x + 2*pi*x*y;
}
.. и обязательно ли переопределить виртуальный метод в производном классе?
Нет, если вы не переопределите метод, класс потомков будет использовать метод, наследуемый от.
Ответ 2
Ключом к пониманию практического использования виртуальных функций является запоминание того, что объекту определенного класса может быть назначен другой объект класса, производный от первого класса объектов.
Например:
class Animal {
public void eat() {...}
}
class FlyingAnimal : Animal {
public void eat() {...}
}
Animal a = new FlyingAnimal();
Класс Animal
имеет функцию eat()
, которая обычно описывает, как животное должно есть (например, положить объект во рту и усвоить).
Однако класс FlyingAnimal
должен определить новый метод eat()
, потому что у летающих животных есть определенный способ съесть.
Итак, возникает вопрос: после того, как я объявил переменную a
типа Animal
и присвоил ей новый объект типа FlyingAnimal
, что будет делать a.eat()
? Какой из двух методов называется?
Ответ здесь: поскольку a
имеет тип Animal
, он будет вызывать метод Animal
. Компилятор немой и не знает, что вы собираетесь назначить объект другого класса переменной a
.
Здесь находится ключевое слово virtual
: если вы объявляете метод как virtual void eat() {...}
, вы в основном говорите компилятору "будьте осторожны, что я делаю здесь некоторые умные вещи, с которыми вы не можете справиться, потому что вы не такой умный". Поэтому компилятор не будет пытаться связать вызов a.eat()
с одним из двух методов, но вместо этого он сообщает системе сделать это во время выполнения!
Таким образом, только когда код выполняется, система будет смотреть a
тип содержимого не на свой объявленный тип и выполняет метод FlyingAnimal
.
Вы можете удивиться: почему, черт возьми, я хочу это сделать? Почему бы не сказать с самого начала FlyingAnimal a = new FlyingAnimal()
?
Причиной этого является то, что, например, у вас может быть много производных классов от Animal
: FlyingAnimal
, SwimmingAnimal
, BigAnimal
, WhiteDog
и т.д. И в какой-то момент вы хотите определить мир, содержащий много Animal
s, поэтому вы говорите:
Animal[] happy_friends = new Animal[100];
У нас есть мир с 100 счастливыми животными.
Вы инициализируете их в какой-то момент:
...
happy_friends[2] = new AngryFish();
...
happy_friends[10] = new LoudSnake();
...
И в конце дня вы хотите, чтобы все съели, пока не заснули. Поэтому вы хотите сказать:
for (int i=0; i<100; i++) {
happy_friends[i].eat();
}
Итак, как вы можете видеть, у каждого животного есть свой метод еды. Только с помощью виртуальных функций вы можете достичь этой функциональности. В противном случае каждый будет вынужден "съесть" точно так же, как описано в самой общей функции eat
внутри класса Animal
.
EDIT:
Это поведение на самом деле по умолчанию на обычных языках высокого уровня, таких как Java.
Ответ 3
Как и любой другой язык... если вы хотите полиморфизм. Для этого есть тонны использования. Например, вы хотите абстрагировать способ чтения ввода с консоли или файла или какого-либо другого устройства. У вас может быть общий интерфейс считывателя, за которым следуют несколько конкретных реализаций с использованием виртуальных функций.
Ответ 4
например. проксирования. т.е. методы перезаписи во время выполнения. Например, NHibernate использует это для поддержки ленивой загрузки.
Ответ 5
Он использовал, чтобы сказать производному классу, что функция может быть переопределена.
MSDN имеет хороший пример здесь.
Ответ 6
В основном виртуальные члены позволяют выражать полиморфизм, производный класс может иметь метод с той же сигнатурой, что и метод в своем базовом классе, а базовый класс будет вызывать метод производного класса.
Основной пример:
public class Shape
{
// A few example members
public int X { get; private set; }
public int Y { get; private set; }
public int Height { get; set; }
public int Width { get; set; }
// Virtual method
public virtual void Draw()
{
Console.WriteLine("Performing base class drawing tasks");
}
}
class Circle : Shape
{
public override void Draw()
{
// Code to draw a circle...
Console.WriteLine("Drawing a circle");
base.Draw();
}
}
class Rectangle : Shape
{
public override void Draw()
{
// Code to draw a rectangle...
Console.WriteLine("Drawing a rectangle");
base.Draw();
}
}
class Triangle : Shape
{
public override void Draw()
{
// Code to draw a triangle...
Console.WriteLine("Drawing a triangle");
base.Draw();
}
}
Ответ 7
Это позволяет достичь позднего связывания, что означает определение во время выполнения, а не во время компиляции, когда будет вызван элемент. См. Wikipedia.
Ответ 8
Из здесь:
В объектно-ориентированном программировании виртуальная функция или виртуальный метод функция или метод, поведение которых может быть переопределено в наследство класс с помощью функции с тем же подпись.
Ответ 9
Например, у вас есть базовый класс Params и набор производных классов. Вы хотите иметь возможность выполнять одну и ту же операцию в массиве, который хранит все возможные классы, полученные из параметров.
Нет проблем - объявите метод виртуальным, добавьте базовую реализацию в класс Params и переопределите его в производных классах. Теперь вы можете просто пересечь массив и вызвать метод через ссылку - будет вызываться правильный метод.
class Params {
public:
virtual void Manipulate() { //basic impl here }
}
class DerivedParams1 : public Params {
public:
override void Manipulate() {
base.Manipulate();
// other statements here
}
};
// more derived classes can do the same
void ManipulateAll( Params[] params )
{
for( int i = 0; i < params.Length; i++ ) {
params[i].Manipulate();
}
}
Ответ 10
Использование виртуальных функций в С#
Виртуальные функции в основном используются для переопределения метода базового класса в производном классе с той же сигнатурой.
Когда производный класс наследует базовый класс, объект производного класса является ссылкой либо на производный класс, либо на базовый класс.
Виртуальные функции разрешаются с опозданием компилятором (т.е. привязкой времени выполнения)
virtual
в базовом классе, наиболее производная реализация класса вызвана в соответствии с фактическим типом упомянутого объекта, независимо от объявленного типа указателя или ссылки. Если это не virtual
, метод разрешен early
, и вызываемая функция выбирается в соответствии с объявленным типом указателя или ссылки.
Ответ 11
Чтение объектно-ориентированного программирования