Практическое использование виртуальных функций в C # - PullRequest
48 голосов
/ 30 июня 2009

Каково практическое использование виртуальных функций в c #?

Ответы [ 9 ]

81 голосов
/ 30 июня 2009

Так что в основном, если в вашем классе предков вы хотите определенное поведение для метода. Если ваш потомок использует тот же метод, но имеет другую реализацию, вы можете переопределить его, если у него есть ключевое слово 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());
   }
}

Редактировать: Вопросы в комментарии
Если я не буду использовать виртуальное ключевое слово в базовом классе, будет ли оно работать?

Если вы используете ключевое слово override в дочерних классах, оно не будет работать. Вы получите ошибку компилятора CS0506 'function1': невозможно переопределить унаследованный член 'function2', поскольку он не помечен как «виртуальный», «абстрактный» или «переопределить»

Если вы не используете переопределение, вы получите CS0108 предупреждение 'desc.Method ()' скрывает унаследованный член 'base.Method ()' Используйте новое ключевое слово, если скрытие было предназначено.

Чтобы обойти это, поместите ключевое слово new перед методом, который вы скрываете .

* * Например, тысяча двадцать восемь
  new public double Area() 
  {
     return 2*pi*x*x + 2*pi*x*y; 
  }

.. и обязательно ли переопределять виртуальный метод в производном классе?
Нет, если вы не переопределите метод, класс-потомок будет использовать метод, от которого он наследует.

64 голосов
/ 29 августа 2013

Ключ к пониманию практического использования виртуальных функций заключается в том, чтобы помнить, что объекту определенного класса может быть присвоен другой объект класса, производный от класса первого объекта.

Например:

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 с, так что вы говорите:

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.

7 голосов
/ 30 июня 2009

Как и любой другой язык .. когда вы хотите полиморфизм. Есть тонны использования для этого. Например, вы хотите абстрагировать способ чтения ввода с консоли, файла или другого устройства. У вас может быть универсальный интерфейс считывателя, а затем несколько конкретных реализаций с использованием виртуальных функций.

4 голосов
/ 30 июня 2009

например. методы проксирования. т.е. перезаписывать методы во время выполнения. Например, NHibernate использует это для поддержки отложенной загрузки.

3 голосов
/ 30 июня 2009

Это позволяет добиться позднего связывания, то есть определять во время выполнения, а не во время компиляции, какой элемент объекта будет вызываться. См. Википедия .

3 голосов
/ 30 июня 2009

В основном виртуальные члены позволяют вам выражать полиморфизм, у производного класса может быть метод с той же сигнатурой, что и у метода в его базовом классе, и базовый класс будет вызывать метод производного класса.

Базовый пример:

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();
    }
}
1 голос
/ 07 октября 2013

Использование виртуальных функций в c #

Виртуальные функции в основном используются для переопределения метода базового класса в производном классе с той же сигнатурой.

Когда производный класс наследует базовый класс, объект производного класса является ссылкой либо на производный класс, либо на базовый класс.

Виртуальные функции разрешаются компилятором поздно (т. Е. Привязка во время выполнения)

virtual в базовом классе реализация функции наиболее производного класса вызывается в соответствии с фактическим типом объекта, на который имеется ссылка, независимо от объявленного типа указателя или ссылки. Если это не virtual, метод разрешается early, и вызываемая функция выбирается в соответствии с объявленным типом указателя или ссылки.

1 голос
/ 30 июня 2009

Например, у вас есть базовый класс 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();
    }
 }
1 голос
/ 30 июня 2009

С здесь :

В объектно-ориентированном программировании виртуальная функция или виртуальный метод функция или метод, поведение которого может быть переопределено в наследовании класс по функции с тем же подпись.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...