Unity:在创建新类实例时出现空值异常

8

我遇到了一个非常糟糕的情况:我正在创建一个泛型类的新实例,但它返回了“奇怪”的null值。

    Rule rule2 = new Rule(); // initiate the class
    Debug.Log(rule2); //1st debug
    rule2.RuleSetup(r: "CaughtEnough", li: 0); //setting up the parameters
    Debug.Log(rule2.rule); //2nd debug

第一个调试给了我

    null
    UnityEngine.Debug:Log(Object)

同时设置参数有效,并且第二次调试给了我结果。

   CaughtEnough
   UnityEngine.Debug:Log(Object)

这应该是在正确类实例中的内容。

目前唯一的问题是,如果在这个规则类实例中调用

   Invoke(rule, 0f);

我的代码报了NullReferenceException异常,但实际上函数是可以正常运行的。

   CaughtEnough();

这个功能正常运作并符合预期。

有什么想法可以解决问题的根源以及如何克服它吗?

根据要求,也发布了Rule类的设置部分,虽然很简单。

public class Rule : MonoBehaviour {

public string rule;

public int leftInt;
public Dictionary<string, int> leftDict;
public float countdown;

public int outcome;

public CatchManager catchMan;
public Net net;

// Use this for initialization
void Start () {
    RuleSetup();   
}

public void RuleSetup(string r = "NoRule", int li = 0, Dictionary<string, int> ld = null,  float cd = float.PositiveInfinity) {
    rule = r;
    leftInt = li;
    leftDict = ld;
    countdown = cd;
}
.....

只需检查rule2是否为空,方法是if(rule2==null){Debug.Log("null")}else{Debug.Log("Not null")}。同时,请发布您的Rule类,以便我们可以查看RuleSetup是什么。 - Programmer
谢谢,我按照你的建议进行了检查,rule2确实为空。 - Alex
2个回答

20
public class Rule : MonoBehaviour{}
Rule rule2 = new Rule();
您不能使用“new”关键字创建新实例,如果您继承自“MonoBehaviour”。
您应该会得到以下异常:

  

您正在尝试使用“new”关键字创建MonoBehaviour。这是不允许的。只能使用AddComponent()添加MonoBehaviour。或者,您的脚本可以继承ScriptableObject或没有任何基类

如果您有public class Rule {},那么您的代码将起作用,但您有public class Rule : MonoBehaviour {}。
创建从MonoBehaviour派生的类的新实例的方法:

示例类:
public class Rule : MonoBehaviour
{
    public Rule(int i)
    {

    }
}

如果你继承自 MonoBehaviour,你应该使用 GameObject.AddComponent 或者 Instantiate 来创建它的新实例。

Rule rule2 = null;
void Start()
{
  rule2 = gameObject.AddComponent<Rule>();
}

或者

public Rule rulePrefab;
Rule rule2;
void Start()
{
    rule2 = Instantiate(rulePrefab) as Rule;
}
如果 Rule 脚本已经存在并附加到游戏对象上,您无需创建/添加/实例化该脚本的新实例。只需使用 GetComponent 函数从其所附加到的游戏对象获取该脚本实例即可。
Rule rule2;
void Start()
{
    rule2 = GameObject.Find("NameObjectScriptIsAttachedTo").GetComponent<Rule>();
}

当从 MonoBehaviour 派生脚本时,您会注意到无法在构造函数中使用该参数。



创建不从MonoBehaviour派生的类的新实例:

示例类:(请注意,它不从“MonoBehaviour”派生)

public class Rule
{
    public Rule(int i)
    {

    }
}

如果你继承MonoBehaviour,那么你应该使用new关键字来创建它的新实例。现在,如果你想的话,你可以在构造函数中使用参数。

Rule rule2 = null;

void Start()
{
    rule2 = new Rule(3);
}
编辑
在最新版本的Unity中,使用new关键字创建一个继承自MonoBehaviour的脚本实例可能不会报错,也不会是null,但所有回调函数都不会执行。这包括AwakeStartUpdate等函数。因此,您仍然需要按照本答案开头提到的方法正确创建它们。

1
不知道,谢谢。对我来说看起来是一个非常奇怪的约定。 - Alex
1
@Alex 新的Unity用户会有这样的经历,我也是其中之一。哦,还要确保在创建Rule脚本内部的CatchManagerNet实例时做同样的事情。 - Programmer
1
再次感谢,其他所有内容实际上都是预设物和工作人员。不知道为什么我认为在规则情况下只使用一个类就可以了 :) - Alex

2

跟进一下,我是如何做到的以及为什么这样做:

  1. 我不再从MonoBehaviour继承Rule类,以避免跟踪游戏对象的创建和删除,因为那是痛苦的。

  2. 由于泛型类中不存在Invoke方法,我使用反射来替换它,如此处所述。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接