初始化容器时C#中的NullReferenceException

时间:2013-10-02 19:58:21

标签: c# .net

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

3 个答案:

答案 0 :(得分:7)

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

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

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

有关详细信息,请参阅我的article on constructor chaining

要学习的经验教训:

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

答案 1 :(得分:3)

尝试:

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

中的

你在使用之前没有实现localSMT,所以它不起作用。

编辑:或

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

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

第二种解决方案更清洁。

答案 2 :(得分:1)

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

确保当您尝试使用initialize他们的对象时!并且不要太担心,这是一个非常common problem的编码。