初始化容器时C#中的NullReferenceException

use*_*956 0 .net c#

在下面列出的C#代码中,我得到一个带有错误的"NullReferenceException":

"你调用的对象是空的"

我猜这个错误与继承和/或模板定义有关.列表被初始化,并且在调试时我可以确认列表没有指向NULL.我无法弄清楚如何以另一种方式做到这一点.(对不起令人困惑的班级名称/结构).这里发生异常:this.localSMT.doSomething(base.list);

public class VTEST<V>
{
    public List<V> list;
    public LocalSMT<V> localSMT;
    public VTEST()
    {
        list = new List<V>();
    }
}
public class VTEST_FSUB<V> : VTEST<V>
{
    public VTEST_FSUB()
    {
        do_virtual();
    }
    public void do_virtual()
    {
        this.localSMT.doSomething(base.list);
    }
}
public class VTEST_RUN : VTEST_FSUB<int>
{
    public VTEST_RUN()
    {
        localSMT = new VTEST_SUB();
    }
}

public class LocalSMT<V>
{
    public LocalSMT() { }
    public virtual void doSomething(List<V> value) { }
}
public class VTEST_SUB : LocalSMT<int>
{
    public VTEST_SUB(){}
    public override void doSomething(List<int> value) { 
            System.Console.WriteLine("VTEST_SUB VIRTUAL");
    }
}
class Program 
{

    Program() {}

    static void Main(string[] args)
    {
        VTEST_RUN run = new VTEST_RUN();
    }
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 7

问题是,在VTEST_FSUB<V>构造函数体被执行之前VTEST_RUN构造体.所以当do_virtual被调用时,localSMT仍然是null.然后do_virtual尝试调用方法localSMT,因此异常.

基本上,层次结构中任何类的初始化顺序是:

  • 初始化已在声明点初始化器内声明的变量(任何其他变量只具有变量类型的默认值)
  • 链到基类初始化
  • 执行构造函数体

有关更多详细信息,请参阅有关构造函数链接的文章.

经验教训:

  • 避免公共领域.如果您使用私有字段,则很容易找到读取它们并写入它们的每段代码
  • 理想情况下,使用readonly字段:如果您将值传递给构造函数链并在VTEST<V>构造函数中设置它,那么您就不会遇到问题.(不可否认readonly,由于下一点,字段仍然会很痛苦......)
  • 避免构造函数中的虚方法调用.在这种情况下,这不是问题,但如果do_virtual已经抽象VTEST_FSUB<V>并重写以调用localSMT.doSomething,您可能很容易遇到相同的问题VTEST_RUN.它仍然会在构造函数体运行之前执行,这将是令人惊讶的.在构造函数中调用的任何内容都在部分初始化的对象上运行,这是一种不稳定的情况.
  • 避免使用大型继承层次结构.他们是一个痛苦的工作和理由.
  • 遵循.NET命名约定!您的代码部分难以阅读,因为它非常简单.即使您只是提供示例代码,至少遵循大写惯例.