c#中虚函数的实际用法

odi*_*seh 49 c# virtual-functions

c#中虚函数的实际用法是什么?

Joh*_*lan 82

所以基本上如果在你的祖先类中你想要一个方法的特定行为.如果您的后代使用相同的方法但具有不同的实现,则可以覆盖它,如果它具有虚拟关键字.

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());
   }
}
Run Code Online (Sandbox Code Playgroud)

编辑:注释中的问题
如果我不在基类中使用虚拟关键字,它会起作用吗?

如果override在后代类中使用关键字,则无法使用.您将生成编译器错误CS0506'function1 ':无法覆盖继承的成员'function2',因为它未标记为"虚拟","抽象"或"覆盖"

如果你不使用覆盖你会得到CS0108警告'desc.Method()'隐藏继承成员'base.Method()'如果想要隐藏,使用new关键字.

为了解决这个问题,请将new关键字放在您隐藏的方法之前.

例如

  new public double Area() 
  {
     return 2*pi*x*x + 2*pi*x*y; 
  }
Run Code Online (Sandbox Code Playgroud)

..并且必须覆盖派生类中的虚方法吗?
不,如果你不重写方法,后代类将使用它继承的方法.

  • @Pre我在评论中回答了你的问题 (4认同)
  • 如果我不在基类中使用虚拟关键字怎么办?它会起作用吗?是否强制覆盖派生类中的虚方法? (3认同)

Bog*_*dru 67

理解虚函数的实际用法的关键是要记住,某个类的对象可以被赋予从第一个对象的类派生的类的另一个对象.

例如:

class Animal {
   public void eat() {...}
}

class FlyingAnimal : Animal {
   public void eat() {...}
}

Animal a = new FlyingAnimal();
Run Code Online (Sandbox Code Playgroud)

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等,并在一个点上,你要定义一个世界包含了许多AnimalS,所以你说:

Animal[] happy_friends = new Animal[100];
Run Code Online (Sandbox Code Playgroud)

我们拥有100个快乐动物的世界.你在某个时候初始化它们:

...
happy_friends[2] = new AngryFish();
...
happy_friends[10] = new LoudSnake();
...
Run Code Online (Sandbox Code Playgroud)

在一天结束时,你希望每个人在睡觉前都能吃.所以你想说:

for (int i=0; i<100; i++) {
   happy_friends[i].eat();
}
Run Code Online (Sandbox Code Playgroud)

如你所见,每只动物都有自己的进食方法.只有使用虚拟功能才能实现此功能.否则,每个人都会被迫以完全相同的方式"吃":如课堂eat内最常见的功能所述Animal.

编辑:这种行为实际上是Java等常见高级语言的默认行为.

  • 那么为什么不让Animal成为一个界面呢?因为所有动物都可以使用动物基础功能? (7认同)
  • 非常好的解释.最佳答案!:) (3认同)
  • 这是迄今为止我见过的"虚拟"的最佳解释.感谢您让它如此直观. (2认同)

Nav*_*een 7

像任何其他语言一样..当你想要多态时.这有很多用处.例如,您想要抽象从控制台或文件或其他设备读取输入的方式.您可以使用通用读取器接口,然后使用虚函数进行多个具体实现.


Bur*_*ard 1

这里

在面向对象编程中,虚函数或虚方法是一种函数或方法,其行为可以在继承类中被具有相同签名的函数覆盖。