基类构造函数的虚函数

san*_*ngh 2 c#

我在下面的代码片段中总结了我的问题

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace St
{
    public  class Animal
    {
        public Animal()
        {
            Speak();
        }
      public virtual void Speak()
      {
          Console.WriteLine("Animal speak");
      }
    }
    public  class Dog:Animal
    {
        private StringBuilder sb = null;

        public Dog()
        {
                sb=new StringBuilder();
        }
        public override void Speak()
        {
            Console.WriteLine("bow...{0}",sb.Append("bow"));
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Dog d=new Dog();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

当我编译它时没有错误但是当我运行它时我得到对象引用错误.

Ree*_*sey 11

问题是你在Animal的构造函数中调用了一个虚方法.

这是一种危险的做法 - 正是出于这个原因.问题是,当你构造一个Dog时,首先运行"Animal"(基类)构造函数.此时,它打电话Speak().但是,Dog的Speak方法依赖于已经运行的Dog的构造函数,因此sb尚未初始化并且仍然存在null.

通常,构造函数中的虚方法调用是一个非常糟糕的想法 - 并且是设计缺陷的标志.我在这里推荐一种不同的方法.


我对此进行重新修改的建议是简单地Speak()从构造函数中删除它.我会把这段代码写成:

public class Animal
{
    public Animal()
    {
        // Don't do this in the constructor
        // Speak();
    }
    public virtual void Speak()
    {
        Console.WriteLine("Animal speak");
    }
}
public class Dog : Animal
{
    private StringBuilder sb = null;

    public Dog()
    {
        sb = new StringBuilder();
    }
    public override void Speak()
    {
        Console.WriteLine("bow...{0}", sb.Append("bow"));
    }
}
class Program
{
    static void Main(string[] args)
    {
        Dog d = new Dog();
        d.Speak();
    }
}
Run Code Online (Sandbox Code Playgroud)

从逻辑的角度来看,这对我来说更有意义.这一行:

Dog d = new Dog();
Run Code Online (Sandbox Code Playgroud)

负责单一行动 - 构建新行动Dog.作为班级的消费者,我不希望它执行复杂的操作(即:说话) - 仅仅是为了正确地创建和设置Dog及其内部状态.

当我想要它发言时,我特意打电话:

d.Speak();
Run Code Online (Sandbox Code Playgroud)