0

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

3 回答 3

7

问题是构造函数主体在构造函数主体之前VTEST_FSUB<V>执行。所以当被调用时,仍然为空。然后尝试在 上调用方法,因此出现异常。VTEST_RUNdo_virtuallocalSMTdo_virtuallocalSMT

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

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

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

教训:

  • 避免公共领域。如果您使用私有字段,则很容易找到读取和写入它们的每一段代码
  • 理想情况下,使用readonly字段:如果您将值传递给构造函数链并将其设置在VTEST<V>构造函数中,则不会有问题。(诚​​然readonly,由于下一点,字段仍然会很痛苦......)
  • 避免在构造函数中调用虚方法。do_virtual在这种情况下,这不是问题,但如果已经抽象 inVTEST_FSUB<V>并覆盖调用localSMT.doSomethingin ,您很容易遇到同样的问题VTEST_RUN。它仍然会在构造函数主体运行之前执行,这将是令人惊讶的。您在构造函数中调用的任何内容都在对部分初始化的对象进行操作,这是一种不稳定的情况。
  • 避免大型继承层次结构。与他们一起工作和推理是一件痛苦的事。
  • 遵循 .NET 命名约定!您的代码部分难以阅读,因为它太单调了。即使您只是提供示例代码,至少也要遵循大写约定。
于 2013-10-02T20:04:02.913 回答
3

尝试:

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

public class VTEST_FSUB<V> : VTEST<V>

您在使用之前没有 instatianing localSMT,所以它不起作用。

编辑:或

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

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

第二种解决方案更清洁。

于 2013-10-02T20:01:01.417 回答
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);
}

确保当您尝试使用初始化它们的对象时!而且不用太担心,这是编码中很常见的问题

于 2013-10-02T20:03:02.990 回答