C#中虚函数的实际使用

发布于 2024-07-25 14:40:47 字数 22 浏览 8 评论 0原文

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

狼亦尘 2024-08-01 14:40:47

所以基本上,如果在您的祖先类中您想要某个方法具有某种行为。 如果您的后代使用相同的方法但具有不同的实现,您可以覆盖它,如果它具有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());
   }
}

编辑:评论中的问题
如果我不在基类中使用 virtual 关键字,它会起作用吗?

如果您在后代类中使用 override 关键字,它将不起作用。 您将生成编译器错误 CS0506 'function1 ' : 无法覆盖继承的成员 'function2',因为它没有标记为“虚拟”、“抽象”或“覆盖”

如果您不使用覆盖,您将得到 CS0108 警告 'desc.Method()' 隐藏继承成员 'base.Method()' 使用new 关键字(如果有意隐藏)。

要解决此问题,请将 new 关键字放在您要隐藏的方法前面。

例如

  new public double Area() 
  {
     return 2*pi*x*x + 2*pi*x*y; 
  }

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

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 override it, If it has a virtual keyword.

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?

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"

If you don't use the override You'll get the CS0108 warning 'desc.Method()' hides inherited member 'base.Method()' Use the new keyword if hiding was intended.

To get around this put the new keyword in front of the method you are hiding.

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.

静谧 2024-08-01 14:40:47

理解虚函数实际用法的关键是要记住,可以将某个类的对象分配给从第一个对象的类派生的类的另一个对象。

例如:

class Animal {
   public void eat() {...}
}

class FlyingAnimal : Animal {
   public void eat() {...}
}

Animal a = new FlyingAnimal();

Animal 类有一个函数eat(),它通常描述动物应该如何进食(例如将物体放入口中并吞咽)。

但是,FlyingAnimal 类应该定义一个新的 eat() 方法,因为飞行动物有特定的进食方式。

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

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

这是 virtual 关键字发挥作用的地方:如果您将该方法声明为 virtual void eat() {...},那么您基本上是在告诉编译器“小心”我在这里做了一些你无法处理的聪明的事情,因为你不那么聪明”。 因此,编译器不会尝试将调用 a.eat() 链接到这两个方法中的任何一个,而是告诉系统在运行时执行此操作!

因此,只有当代码执行时,系统才会查看a内容类型而不是其声明的类型,并执行FlyingAnimal的方法。

你可能想知道:我到底为什么要这么做? 为什么不从一开始就说 FlyingAnimal a = new FlyingAnimal() 呢?

原因是,例如,您可能有许多来自 Animal 的派生类:FlyingAnimalSwimmingAnimalBigAnimal code>、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();
}

正如你所看到的,每种动物都有自己的饮食方法。 只有使用虚拟函数才能实现此功能。 否则,每个人都将被迫以完全相同的方式“吃”:如 Animal 类中最通用的 eat 函数中所述。

编辑:
这种行为实际上是 Java 等常见高级语言中的默认

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).

However, the FlyingAnimal class should define a new eat() method, because flying animals have a particular way of eating.

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?

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.

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!

So only when the code executes, the system will look at a's content type not at its declared type and executes FlyingAnimal's method.

You may wonder: why the hell would I want to do that? Why not say right from the start 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 Animals, so you say:

Animal[] happy_friends = new Animal[100];

We have a world with 100 happy animals.
You initialize them at some point:

...
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 virtual functions 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.

EDIT:
This behavior is actually default in common high-level languages like Java.

枕梦 2024-08-01 14:40:47

就像任何其他语言一样......当你想要多态性时。 这有很多用途。 例如,您想要抽象从控制台、文件或其他设备读取输入的方式。 您可以拥有一个通用阅读器接口,然后使用虚函数进行多个具体实现。

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.

野却迷人 2024-08-01 14:40:47

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

e.g. proxying methods. i.e. overwriting methods at runtime. For example, NHibernate uses this to support lazy loading.

迷途知返 2024-08-01 14:40:47

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

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.

各空 2024-08-01 14:40:47

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

一个基本的例子:

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

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();
    }
}
十雾 2024-08-01 14:40:47

来自此处

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

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.

秋意浓 2024-08-01 14:40:47

例如,您有一个基类 Params 和一组派生类。 您希望能够对存储从参数派生的所有可能的类的数组执行相同的操作。

没问题 - 将方法声明为虚拟,向 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();
    }
 }

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.

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.

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();
    }
 }
故事与诗 2024-08-01 14:40:47

C#中虚函数的使用

虚函数大多用于在派生类中用相同的签名重写基类的方法。

当派生类继承基类时,派生类的对象是对派生类或基类的引用。

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

virtual,根据所引用对象的实际类型调用最派生类的函数实现,无论指针或引用的声明类型如何。 如果它不是虚拟,则该方法会早期解析,并根据指针或引用的声明类型选择调用的函数。

Use of virtual functions in 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.

深陷 2024-08-01 14:40:47

示例

让我们考虑一下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();
        }   
    }
}

前面代码的输出是:

VirtualMembersArticle.Company

让我们考虑一下,我们想要更改从 Company 类中的 System.Object 继承的 ToString() 方法的标准行为。 为了实现这个目标,使用 override 关键字来声明该方法的另一个实现就足够了。

public class Company
{
    ...
    public override string ToString()
    {
        return $"Name: {this.Name}";
    }         
}

现在,当调用虚拟方法时,运行时将检查其派生类中是否有重写成员,如果存在则调用它。 我们的应用程序的输出将是:

Name: Microsoft

事实上,如果您检查 System.Object 类,您会发现该方法被标记为虚拟。

namespace System
{
    [NullableContextAttribute(2)]
    public class Object
    {
        ....
        public virtual string? ToString();
        ....
    }
}

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.

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.

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.

namespace System
{
    [NullableContextAttribute(2)]
    public class Object
    {
        ....
        public virtual string? ToString();
        ....
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文