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)
..并且必须覆盖派生类中的虚方法吗?
不,如果你不重写方法,后代类将使用它继承的方法.
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等常见高级语言的默认行为.