在C#中实用虚函数的用途

10 浏览
0 Comments

在C#中实用虚函数的用途

在C#中,虚函数有什么实际的用途?

admin 更改状态以发布 2023年5月21日
0
0 Comments

理解虚函数的实际用途的关键是记住:某个类的对象可以被赋给该对象派生类的对象。例如:

class Animal {
   public void eat() {...}
}
class FlyingAnimal : Animal {
   public void eat() {...}
}
Animal a = new FlyingAnimal();

。Animal类有一个函数eat(),通常描述了动物如何进食(例如把物体放到嘴里然后吞下去)。然而,FlyingAnimal类应该定义一种新的eat()方法,因为飞行动物有一种特殊的进食方式。因此,在这里提出的问题是,在我声明了类型为Animal的变量a并将其分配给类型为FlyingAnimal的新对象之后,a.eat()会做什么?哪种方法被调用了?答案是:由于a是类型为Animal的,它将调用Animal的方法。编译器很蠢,不知道你将一个不同类的对象分配给a变量。这就是虚拟关键字发挥作用的地方:如果将方法声明为virtual void eat() {...},你基本上是告诉编译器“小心,我正在做一些聪明的事情,你无法处理,因为你不如我聪明”。因此,编译器不会尝试将调用a.eat()链接到这两个方法中的任何一个,而是告诉系统在运行时执行!因此,只有当代码执行时,系统才会查看a的内容类型而不是它声明的类型,并执行FlyingAnimal的方法。你可能会想,为什么我要这样做?为什么不从一开始就说 FlyingAnimal a = new FlyingAnimal() ?原因是,例如,你可能有很多从Animal派生出来的类:FlyingAnimalSwimmingAnimalBigAnimalWhiteDog等等。在某一时刻,你想定义一个包含许多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();
}

因此,你可以看到,每种动物都有自己的进食方式。只有使用虚函数才能实现这种功能。否则,每个人都会被迫以与Animal类内最一般的eat函数描述的方式相同地“进食”。

编辑:
事实上,这种行为在像Java这样的常见高级语言中是默认的。

0
0 Comments

基本上,如果您的祖先类中某个方法需要特定的行为。如果您的后代使用相同的方法但具有不同的实现,那么如果它具有“虚拟”关键字,您可以进行“覆盖”。

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”关键字,则不起作用。您将生成编译器错误“函数1”:无法覆盖继承的成员“函数2”,因为它未标记为“virtual”、“abstract”或“override”CS0506

如果您不使用覆盖,则会收到警告CS0108“desc.Method()”隐藏了继承的成员“base.Method()”,如果有意隐藏,请使用new关键字。

要解决此问题,请在您要隐藏的方法前面放置“new”关键字。

例如:

  new public double Area() 
  {
     return 2*pi*x*x + 2*pi*x*y; 
  }

是否必须在派生类中覆盖虚拟方法?
不需要,如果您不覆盖该方法,则后代类将使用它继承的方法。

0