c#中虚拟函数的实际用法

问题描述 投票:51回答:10

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

c# virtual-functions
10个回答
83
投票
所以基本上,如果在您的祖先类中,您希望某个方法具有某种行为。如果您的后代使用相同的方法但实现方式不同,则可以对其进行

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; }

..是否必须重写派生类中的虚拟方法?不,如果不重写该方法,则后代类将使用其继承的方法。

0
投票

71
投票
例如:

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派生的类: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(); }

如您所见,每只动物都有自己的进食方法。只有使用

virtual函数才能实现此功能。否则,每个人都将被迫以完全相同的方式“进食”:如eat类中最通用的Animal函数中所述。

编辑:在像Java这样的通用高级语言中,此行为实际上是

default。


7
投票

例如代理方法。即在运行时覆盖方法。例如,NHibernate使用它来支持延迟加载。


4
投票

这允许实现后期绑定,这意味着在运行时而不是在编译时确定将调用哪个对象的成员。参见Wikipedia


4
投票

基本上是虚拟成员,您可以表达多态性,派生类可以具有与其基类中的方法具有相同签名的方法,并且基类将调用派生类的方法。


3
投票
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
投票
在面向对象的程序设计中,虚函数或虚方法是其行为的功能或方法可以在继承中覆盖通过具有相同功能的类签名。

1
投票
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
投票
[当派生类继承基类时,派生类的对象是对派生类或基类的引用。

虚拟函数由编译器解析较晚(即运行时绑定)

virtual在基类中,将根据所引用对象的实际类型调用函数的最派生类的实现,而不考虑指针或引用的声明类型。如果不是virtual,则解析该方法early,然后根据所声明的指针或引用的类型来选择调用的函数。

© www.soinside.com 2019 - 2024. All rights reserved.