初始化容器时 C# 中的 NullReferenceException

本文关键字:中的 NullReferenceException 初始化 | 更新日期: 2023-09-27 18:33:34

在下面列出的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();
    }
}

初始化容器时 C# 中的 NullReferenceException

问题是 VTEST_FSUB<V> 构造函数体在VTEST_RUN构造函数体之前执行。因此,当调用do_virtual时,localSMT仍然是空的。然后do_virtual尝试在localSMT上调用一个方法,因此异常。

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

    初始化在声明
  • 点在初始值设定项中声明的变量(任何其他变量仅具有变量类型的默认值(
  • 链接到基类初始化
  • 执行构造函数体

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

要吸取的教训:

  • 避免公共字段。如果您使用私有字段,则很容易找到读取和写入它们的每一段代码
  • 理想情况下,使用readonly字段:如果您将值传递到构造函数链并将其设置在VTEST<V>构造函数中,则不会有问题。(诚然,readonly字段仍然会因为下一点而痛苦......
  • 避免在构造函数中调用虚拟方法。在这种情况下,这不是问题,但是如果do_virtualVTEST_FSUB<V>中是抽象的,并且被覆盖以在VTEST_RUN中调用localSMT.doSomething,则很容易遇到相同的问题。它仍然会在构造函数主体运行之前执行,这将是令人惊讶的。您在构造函数中调用的任何内容都在部分初始化的对象上运行,这是一个不稳定的情况。
  • 避免大型继承层次结构。与他们一起工作和推理是一种痛苦。
  • 遵循 .NET 命名约定!你的代码部分难以阅读,因为它太不合时宜了。即使您只是提供示例代码,至少也要遵循大写约定。

尝试:

public void do_virtual()
{
    localSMT=new LocalSMT<V>();
    localSMT.doSomething(list);
}

public class VTEST_FSUB<V> : VTEST<V>

您在使用之前没有安装本地SMT,因此它不起作用。

编辑:或

public class VTEST<V>
{
    public List<V> list;
    public LocalSMT<V> localSMT;
    public VTEST()
    {
        list = new List<V>();
        localSMT = new LocalSMT<V>();
    }
}

最好在构造函数中初始化它。

第二种解决方案是更清洁。

public class VTEST_RUN : VTEST_FSUB<int>
{
   public VTEST_RUN()
   {
    localSMT = new VTEST_SUB();  // BAD!  localSMT isn't initialized yet!
    }
}

我相信你没有new你的一个对象:

public void do_virtual()
{
   localSMT = new LocalSMT<V>();
   localSMT.doSomething(list);
}

确保在尝试使用对象时初始化它们! 而且不要太担心,这是编码中非常普遍的问题。