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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 07:18:35  来源:igfitidea点击:

Practical usage of virtual functions in c#

c#virtual-functions

提问by odiseh

What 's the practical usage of virtual functions in c#?

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

回答by Burkhard

From here:

这里

In object-oriented programming, a virtual function or virtual method is a function or method whose behavior can be overridden within an inheriting class by a function with the same signature.

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

回答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 overridekeyword 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 newkeyword 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

This allows to achieve late binding, meaning to determine at runtime rather than at compile-time which object's member will be invoked. See Wikipedia.

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

回答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 Animalclass 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 FlyingAnimalclass 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 aof type Animaland asigned it a new object of type FlyingAnimal, what will a.eat()do? Which of the two methods is called?

所以这里想到的问题是:在我声明了atype的变量Animal并将它分配给了一个新的 type 对象之后FlyingAnimal,会a.eat()做什么?调用这两种方法中的哪一种?

The answer here is: because ais 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 avariable.

这里的答案是:因为a是 type Animal,它会调用Animal的方法。编译器很笨,不知道您要将另一个类的对象分配给a变量。

Here is where the virtualkeyword 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, WhiteDogetc. And at one point you want to define a world containing many Animals, so you say:

其原因是,例如,你可以有很多派生类来自AnimalFlyingAnimalSwimmingAnimalBigAnimalWhiteDog等,并在一个点上,你要定义包含许多世界AnimalS,所以你说:

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 eatfunction inside the Animalclass.

如您所见,每种动物都有自己的进食方法。只有使用函数才能实现这个功能。否则,每个人都将被迫以完全相同的方式“吃”:如类中最通用的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)

虚函数由编译器后期解析(即运行时绑定)

virtualin 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 earlyand 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();
        ....
    }
}