c#中虚拟函数的实际用法是什么?
override,如果其具有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
关键字放在您正在[[隐藏
例如
new public double Area()
{
return 2*pi*x*x + 2*pi*x*y;
}
..是否必须重写派生类中的虚拟方法?不,如果不重写该方法,则后代类将使用其继承的方法。
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(); }
如您所见,每只动物都有自己的进食方法。只有使用virtual函数才能实现此功能。否则,每个人都将被迫以完全相同的方式“进食”:如
eat
类中最通用的Animal
函数中所述。编辑:在像Java这样的通用高级语言中,此行为实际上是default。
例如代理方法。即在运行时覆盖方法。例如,NHibernate使用它来支持延迟加载。
这允许实现后期绑定,这意味着在运行时而不是在编译时确定将调用哪个对象的成员。参见Wikipedia。
基本上是虚拟成员,您可以表达多态性,派生类可以具有与其基类中的方法具有相同签名的方法,并且基类将调用派生类的方法。
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();
}
}
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();
}
}
虚拟函数由编译器解析较晚(即运行时绑定)
virtual
在基类中,将根据所引用对象的实际类型调用函数的最派生类的实现,而不考虑指针或引用的声明类型。如果不是virtual
,则解析该方法early
,然后根据所声明的指针或引用的类型来选择调用的函数。