关于C#:虚拟函数


Virtual functions

我不太明白-我什么时候应该使用虚拟函数?如果有人能向我解释,我会很高兴的,谢谢。


虚拟方法是多态性的关键。标记为virtual的方法可以在派生类中重写,以更改或专门化类的行为。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Base
{
    public virtual void SayHello()
    {
        Console.WriteLine("Hello from Base");
    }
}

class Derived : Base
{
    public override void SayHello()
    {
        Console.WriteLine("Hello from Derived");
    }
}

static void Main()
{
    Base x = new Base();
    x.SayHello(); // Prints"Hello from Base"
    x = new Derived();
    x.SayHello(); // Prints"Hello from Derived"
}

请注意,您可以重新声明(而不是重写)一个不是虚拟的方法,但在这种情况下,它不会参与多态性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Base
{
    public void SayHello() // Not virtual
    {
        Console.WriteLine("Hello from Base");
    }
}

class Derived : Base
{
    public new void SayHello() // Hides method from base class
    {
        Console.WriteLine("Hello from Derived");
    }
}

static void Main()
{
    Base x = new Base();
    x.SayHello(); // Prints"Hello from Base"
    x = new Derived();
    x.SayHello(); // Still prints"Hello from Base" because x is declared as Base
    Derived y = new Derived();
    y.SayHello(); // Prints"Hello from Derived" because y is declared as Derived
}


可能通过一个例子最容易理解,所以imagin我们有下面这样的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Base{
public virtual string VirtualMethod(){
    return"base virtual";
}

public string NotVirtual(){
    return"base non virtual";
}
}

class Derived : Base{
  public override string VirtualMethod(){
        return"Derived overriden";
  }
  public new string NotVirtual(){
        return"new method defined in derived";
  }
}

}

如果您使用下面的代码

1
2
3
4
5
6
7
8
9
10
11
  Base b = new Base();
  Derived d = new Derived();
  Base b2 = d;

  Console.WriteLine(b.VirtualMethod());
  Console.WriteLine(d.VirtualMethod());
  Console.WriteLine(b2.VirtualMethod());

  Console.WriteLine(b.NotVirtual());
  Console.WriteLine(d.NotVirtual());
  Console.WriteLine(b2.NotVirtual());

值得注意的是,b2 a和d是完全相同的物体!

上述结果将是:

基础虚拟

派生重写

派生重写

基本非虚拟

派生中定义的新方法

基本非虚拟

尽管最后两行在同一对象上调用了方法名notvirtual。因为TE变量声明为基变量和派生变量,而方法不是虚拟变量,所以声明的变量类型决定了调用的方法,而如果方法是虚拟的,则对象的运行时类型是决定将为哪个方法调用的因素。


虚拟函数是子类可以根据需要重写的函数。

父母亲

1
2
3
4
    public virtual string someMethod()
    {
        return"someting";
    }

儿童

1
2
3
4
    public override string someMethod()
    {
        return"someting else";
    }


给定一个基类:

1
2
3
4
5
6
7
class SomeBaseClass()
{
    public virtual string GetName()
    {
         return"SomeBaseClass";
    }
}

当您重写它时,您将继承该函数

1
2
3
class SomeDerivedClass() : SomeBaseClass
{
}

所以当你说:

1
2
SomeDerivedClass sdc = new SomeDerivedClass();
Console.WriteLine(sdc.GetName()); //outputs"SomeBaseClass"

GetName()返回"somebaseclass"

不过,你可以用override这个词。

1
2
3
4
5
6
7
class SomeDerivedClass()
{
    public override string GetName()
    {
         return"SomeDerivedClass";
    }
}

这里,EDOCX1[4]将返回"somederivedclass"


如果已经设置了继承链,并且希望重写派生类中基类中定义的函数的默认实现,则只需要虚拟函数。

典型的例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Animal
{
    public virtual void Speak()
    {
        Console.WriteLine("...");
    }
}

public class Dog : Animal
{
    public override void Speak()
    {
        Console.WriteLine("Bow-wow");
    }
}

public class Human : Animal
{
    public override void Speak()
    {
        Console.WriteLine("Hello, how are you?");
    }
}

DogHuman类都继承了基础Animal类,因为它们都是动物。但它们都以不同的方式说话,因此它们需要覆盖Speak功能以提供自己独特的实现。

在某些情况下,在设计自己的类时使用相同的模式是有益的,因为这样可以实现多态性,这在本质上是不同的类共享一个公共接口,并且可以通过代码进行类似的处理。

但我会重复其他人在评论中的建议:正确地学习面向对象编程并不是通过问一些堆栈溢出问题就能做到的。这是一个复杂的话题,值得花时间作为开发人员学习。我强烈建议阅读一本关于面向对象编程的书(尤其是一本为C语言编写的书),并仔细阅读示例。OOP是一个非常强大的工具,当正确使用,但绝对可以成为一个障碍时,设计糟糕!


如果希望更改(重写)子类中函数的行为,则使用虚拟函数。

1
2
3
4
5
6
7
8
9
class Order
{
    public virtual long GetOrderNumber { return nextOrderNumber; }
}

class ExpressOrder : Order
{
    public override long GetOrderNumber { return nextOrderNumber + 1000000; }
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public abstract class Weapon
{
    public virtual abstract FireOnTarget();
}

public class Pistol : Weapon
{
    public override FireOnTarget()
    {
        LoadPowder();
        LoadBullet();
        CockIt();
        PullTheTrigger();
    }
}

public class SucideBomber : Weapon
{
    public override FireOnTarget()
    {
        BuyTruck();
        LoadWithC4();
        DriveToTarget();
        ActivateDetonator();
    }
}

好了,现在你们有两个班。重点是在不知道实际类的情况下引用虚函数,例如:

1
2
3
4
5
6
public void PerformFiring(Weapon W)
{
    // do stuff
    W.FireOnTarget();
    // do more stuff
}

此方法将使用您发送的、从武器派生的任何对象,并对该对象调用FireOnTarget。例如:

1
2
Pistol p=new Pistol();
PerformFiring(p);