c#中虚函数的实际使用
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1062102/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Practical usage of virtual functions in c#
提问by odiseh
What 's the practical usage of virtual functions in c#?
c#中虚函数的实际用途是什么?
回答by Burkhard
回答by Naveen
Like any other language..when you want polymorphism. There are tons of usage for this. For example you want to abstract the way input is read from a console or a file or some other device. You can have a generic reader interface followed by multiple concrete implementations using virtual functions.
像任何其他语言一样......当你想要多态时。这有很多用途。例如,您想抽象从控制台、文件或其他设备读取输入的方式。您可以拥有一个通用的阅读器接口,然后是多个使用虚函数的具体实现。
回答by Joachim Kerschbaumer
e.g. proxying methods. i.e. overwriting methods at runtime. For example, NHibernate uses this to support lazy loading.
例如代理方法。即在运行时覆盖方法。例如,NHibernate 使用它来支持延迟加载。
回答by sharptooth
For example you have a base class Params and a set of derived classes. You want to be able to perform the same operation on an array that stores all possible classes derived from params.
例如,您有一个基类 Params 和一组派生类。您希望能够对存储从 params 派生的所有可能类的数组执行相同的操作。
No problem - declare the method virtual, add some basic implementation to Params class and override it in derived classes. Now you can just traverse the array and call the method through the reference - the correct method will be called.
没问题 - 将方法声明为 virtual,向 Params 类添加一些基本实现并在派生类中覆盖它。现在您可以遍历数组并通过引用调用方法 - 将调用正确的方法。
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();
}
}
回答by CMS
Basically virtual members allow you to express polymorphism, a derived class can have a method with the same signature as the method in its base class, and the base class will call the derived class's method.
基本上虚拟成员允许你表达多态,派生类可以有一个与基类中的方法具有相同签名的方法,基类将调用派生类的方法。
A basic example:
一个基本的例子:
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();
}
}
回答by Johnno Nolan
So basically if in your ancestor class you want a certain behaviour for a method. If your descendent uses the same method but has a different implementation you can overrideit, If it has a virtualkeyword.
所以基本上如果在你的祖先类中你想要一个方法的某种行为。如果您的后代使用相同的方法但具有不同的实现,您可以覆盖它,如果它有一个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());
}
}
Edit:Questions in comment
If I don't use virtual keyword in base class, will it work?
编辑:评论中的问题
如果我不在基类中使用 virtual 关键字,它会起作用吗?
If you use the override
keyword in your descendent classes it will not work. You will generate compiler error CS0506'function1' : cannot override inherited member 'function2' because it is not marked "virtual", "abstract", or "override"
如果您override
在后代类中使用关键字,它将不起作用。您将生成编译器错误CS0506'function1':无法覆盖继承的成员 'function2',因为它没有标记为“虚拟”、“抽象”或“覆盖”
If you don't use the override You'll get the CS0108warning 'desc.Method()' hides inherited member 'base.Method()' Use the new keyword if hiding was intended.
如果您不使用覆盖,您将收到CS0108警告 'desc.Method()' hides继承的成员 'base.Method()' 如果打算隐藏,请使用 new 关键字。
To get around this put the new
keyword in front of the method you are hiding.
要解决这个问题,请将new
关键字放在您要隐藏的方法前面。
e.g.
例如
new public double Area()
{
return 2*pi*x*x + 2*pi*x*y;
}
..and is it compulsory to override a virtual method in derived class?
No, if you don't override the method, the descendent class will use method it is inheriting from.
..并且是否必须覆盖派生类中的虚拟方法?
不,如果您不覆盖该方法,后代类将使用它继承的方法。
回答by Juri
回答by Bogdan Alexandru
The key to understanding the practical usage of virtual functions is to keep in mind that an object of a certain class can be assigned another object of a class derived from the first object's class.
理解虚函数实际用法的关键是要记住,可以将某个类的对象分配给从第一个对象的类派生的类的另一个对象。
E.g.:
例如:
class Animal {
public void eat() {...}
}
class FlyingAnimal : Animal {
public void eat() {...}
}
Animal a = new FlyingAnimal();
The Animal
class has a function eat()
that generally describes how an animal should eat (e.g. put the object in mouth and swallow).
该Animal
班有一个函数eat()
通常描述的动物应该怎么吃(如放入口中吞下的对象)。
However, the FlyingAnimal
class should define a new eat()
method, because flying animals have a particular way of eating.
但是,FlyingAnimal
该类应该定义一个新eat()
方法,因为飞行动物有一种特殊的进食方式。
So the question that comes to mind here is: after I declared the variable a
of type Animal
and asigned it a new object of type FlyingAnimal
, what will a.eat()
do? Which of the two methods is called?
所以这里想到的问题是:在我声明了a
type的变量Animal
并将它分配给了一个新的 type 对象之后FlyingAnimal
,会a.eat()
做什么?调用这两种方法中的哪一种?
The answer here is: because a
is of type Animal
, it will call Animal
's method. The compiler is dumb and doesn't know that you are going to assign an object of another class to the a
variable.
这里的答案是:因为a
是 type Animal
,它会调用Animal
的方法。编译器很笨,不知道您要将另一个类的对象分配给a
变量。
Here is where the virtual
keyword comes in action: if you declare the method as virtual void eat() {...}
, you are basically telling the compiler "be careful that I am doing some clever stuff here that you cannot handle because you're not as smart". So the compiler will not attempt to link the call a.eat()
to either of the two methods, but instead it tells the system to do it at runtime!
这里是virtual
关键字起作用的地方:如果你将方法声明为virtual void eat() {...}
,你基本上是在告诉编译器“小心我在这里做了一些你无法处理的聪明的事情,因为你没有那么聪明”。因此,编译器不会尝试将调用链接a.eat()
到这两种方法中的任何一种,而是告诉系统在运行时执行此操作!
So only when the code executes, the system will look at a
's content typenot at its declared type and executes FlyingAnimal
's method.
所以只有当代码执行时,系统才会查看a
的内容类型而不是其声明的类型并执行FlyingAnimal
的方法。
You may wonder: why the hell would I want to do that? Why not say right from the start FlyingAnimal a = new FlyingAnimal()
?
你可能想知道:我到底为什么要这样做?为什么不从一开始就说对FlyingAnimal a = new FlyingAnimal()
?
The reason for that is that, for example, you may have many derived classes from Animal
: FlyingAnimal
, SwimmingAnimal
, BigAnimal
, WhiteDog
etc. And at one point you want to define a world containing many Animal
s, so you say:
其原因是,例如,你可以有很多派生类来自Animal
:FlyingAnimal
,SwimmingAnimal
,BigAnimal
,WhiteDog
等,并在一个点上,你要定义包含许多世界Animal
S,所以你说:
Animal[] happy_friends = new Animal[100];
We have a world with 100 happy animals. You initialize them at some point:
我们有一个拥有 100 只快乐动物的世界。你在某个时候初始化它们:
...
happy_friends[2] = new AngryFish();
...
happy_friends[10] = new LoudSnake();
...
And at the end of the day, you want everybody to eat before they go to sleep. So you want to say:
归根结底,您希望每个人都在睡觉前吃饭。所以你想说:
for (int i=0; i<100; i++) {
happy_friends[i].eat();
}
So as you can see, each animal has its own method of eating. Only by using virtualfunctions can you achieve this functionality. Otherwise, everyone would be forced to "eat" in the exact same way: as described in the most general eat
function inside the Animal
class.
如您所见,每种动物都有自己的进食方法。只有使用虚函数才能实现这个功能。否则,每个人都将被迫以完全相同的方式“吃”:如类中最通用的eat
函数中所述Animal
。
EDIT: This behavior is actually defaultin common high-level languages like Java.
编辑:此行为实际上是 Java 等常见高级语言中的默认行为。
回答by Divya
Use of virtual functions in c#
c#中虚函数的使用
Virtual functions are mostly used to override the base class method in the derived class with the same signature.
虚函数主要用于覆盖派生类中具有相同签名的基类方法。
When a derived class inherits the base class, the object of derived class is a reference to either the derived class or the base class.
当派生类继承基类时,派生类的对象是对派生类或基类的引用。
Virtual functions are resolved late by the compiler(i.e run-time binding)
虚函数由编译器后期解析(即运行时绑定)
virtual
in the base class, the most-derived class's implementation of the function is called according to the actual type of the object referred to, regardless of the declared type of the pointer or reference. If it is not virtual
, the method is resolved early
and the function called is selected according to the declared type of the pointer or reference.
virtual
在基类中,根据所引用对象的实际类型调用最派生类的函数实现,而不管指针或引用的声明类型如何。如果不是virtual
,则解析方法early
并根据指针或引用的声明类型选择调用的函数。
回答by Ivan Porta
Example
例子
Let's consider the ToString()method in System.Object. Because this method is a member of System.Object it's inherited in all classes and will provide the ToString() methods to all of them.
让我们考虑一下System.Object 中的ToString()方法。因为此方法是 System.Object 的成员,所以它在所有类中都被继承,并将为所有类提供 ToString() 方法。
namespace VirtualMembersArticle
{
public class Company
{
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
Company company = new Company() { Name = "Microsoft" };
Console.WriteLine($"{company.ToString()}");
Console.ReadLine();
}
}
}
The output of the previous code is:
前面代码的输出是:
VirtualMembersArticle.Company
Let's consider that we want to change the standard behavior of the ToString() methods inherited from System.Object in our Company class. To achieve this goal it's enough to use the override keyword to declare another implementation of that method.
让我们考虑一下,我们想要更改从 Company 类中的 System.Object 继承的 ToString() 方法的标准行为。为了实现这个目标,使用 override 关键字来声明该方法的另一个实现就足够了。
public class Company
{
...
public override string ToString()
{
return $"Name: {this.Name}";
}
}
Now, when a virtual method is invoked, the run-time will check for an overriding member in its derived class and will call it if present. The output of our application will then be:
现在,当调用虚拟方法时,运行时将检查其派生类中的覆盖成员,并在存在时调用它。我们的应用程序的输出将是:
Name: Microsoft
In fact, if you check the System.Object class you will find that the method is marked as virtual.
实际上,如果您检查 System.Object 类,您会发现该方法被标记为虚拟。
namespace System
{
[NullableContextAttribute(2)]
public class Object
{
....
public virtual string? ToString();
....
}
}