C# 如何通过单击访问单例变量而不导致NullReferenceException

C# 如何通过单击访问单例变量而不导致NullReferenceException,c#,unity3d,C#,Unity3d,这是我的独生子女课程,它启动了E using System.Collections.Generic; using UnityEngine; public class Player : MonoBehaviour { static public Player E; public float EA = 5; public float EG = 0; // Start is called before the first frame update void

这是我的独生子女课程,它启动了E

using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{

    static public Player E;
    public float EA = 5;
    public float EG = 0;

    // Start is called before the first frame update
    void Awake()
    {
        if (E == null)
        {
            E = this;
        }
        else
        {
            Debug.LogError("Hero.Awake() - attempted to assign second Hero.S");
        }
    }

    // Update is called once per frame
    void Update()
    {


        if (EG >= 5 && EA >= 0)
        {
            EG = 0;
        }
        if (EA <= 4 && EG > 9)
        {
            EA = 5;
        }

    }
}
使用System.Collections.Generic;
使用UnityEngine;
公共类玩家:单一行为
{
静态公共播放器E;
公共浮动EA=5;
公共浮动EG=0;
//在第一帧更新之前调用Start
无效唤醒()
{
如果(E==null)
{
E=这个;
}
其他的
{
Debug.LogError(“Hero.Awake()-试图分配第二个Hero.S”);
}
}
//每帧调用一次更新
无效更新()
{
如果(例如>=5&&EA>=0)
{
EG=0;
}
如果(EA 9)
{
EA=5;
}
}
}
现在,当我试图通过OnPointerClick()访问它,试图修改UseItem()方法中的变量EA和EG时,系统将生成一个NullReferenceException。我尝试过切换执行顺序,但没有成功。是因为我想修改它吗?很抱歉,我是Unity的新手,不知道这些事情

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;


public class Slot : MonoBehaviour , IPointerClickHandler

{
    public int ID;
    public string type;
    public string description;
    public bool empty;
    public Texture2D icon;
    // Start is called before the first frame update
    public void OnPointerClick (PointerEventData pointerEventData)
    {
        useItem();
        Debug.Log("clicked" + transform.name);
    }
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {

    }
    public void useItem()
    {
        for (int i = 0; i < 10; i++)
        {
            if (transform.name == "slot(" + (i + 1) + ")")
            {
                Player.E.EA = i;
            }
        }
        Debug.Log(Player.E.EA);
    }
}
使用系统集合;
使用System.Collections.Generic;
使用UnityEngine;
使用UnityEngine.EventSystems;
公共类插槽:MonoBehavior,IPoClickHandler
{
公共int ID;
公共字符串类型;
公共字符串描述;
公共布尔空;
公共纹理2D图标;
//在第一帧更新之前调用Start
PointerEventData PointerEventData上的公共无效点单击(PointerEventData PointerEventData)
{
useItem();
Log(“单击”+transform.name);
}
void Start()
{
}
//每帧调用一次更新
无效更新()
{
}
公用项目()
{
对于(int i=0;i<10;i++)
{
if(transform.name==“插槽(“+(i+1)+”)
{
Player.E.EA=i;
}
}
Debug.Log(Player.E.EA);
}
}

在此行中修改您的代码

static public Player E;
使用此代码

private static Player e;

public static Player E
{
    get
    {
       if(e==null) 
        {
              e= new Player() ;
        } 
       return e;
   } 
    set
    {
      e=value;
    } 
} 

这看起来更像是一个统一/单一行为问题,而不是一个C#问题。 唤醒在游戏开始前触发,仅一次。 可以将其视为初始值设定项

由于您有一个公共的E,您需要为它提供一个实例:通过在Unity IDE接口中添加一个对象(拖放)或通过代码

您也可以使用标记。 在这里,您可以找到有关如何操作的更多信息:

我会选择惰性初始化属性,但如果您真的希望它是一个单例,它应该是这样的

private static Player _instance;
public static Player E
{
    get
    {
        // if already exists return it directly
        if(_instance) return _instance;

        // otherwise find it in the scene
        _instance = FindObjectOfType<Player>();

        // if found return it
        if(_instance) return _instance;

        // otherwise create it now
        // note that anything you have to configure via the Inspector
        // will not be there so you would need to find another way to 
        // initialize all values
        _instance = new GameObject ("Player").AddComponent<Player>();

        return _instance;
    }
}

private void Awake()
{
    if(_instance && _instance != this)
    {
        // another instance already exists!
        Destroy (gameObject);
        return;
    }

    _instance = this;
}
而是将
EA
EG
也设置为属性,并仅在实际更改值时进行检查:

private int _ea = 5;
private int _eg = 0;

public int EA
{
    get => _ea;
    set
    {
        _ea = value;
        if (_eg >= 5 && _ea >= 0) _eg = 0;
        if (_ea <= 4 && _eg > 9) _ea = 5;
    }
}

public int EG
{
    get => _eg;
    set
    {
        _eg = value;
        if (_eg >= 5 && _ea >= 0) _eg = 0;      
        if (_ea <= 4 && _eg > 9) _ea = 5;
    }
}
执行相应的setter,并根据事件驱动更新备份字段


现在,是的,如果你甚至需要将其作为一种
单一行为
。。。实际上,您甚至不需要实例,因此根本不需要单例

public static class Player
{
    private static int _ea = 5;
    private static int _eg = 0;

    public static int EA
    {
        get => _ea;
        set
        {
            _ea = value;
            if (_eg >= 5 && _ea >= 0) _eg = 0;          
            if (_ea <= 4 && _eg > 9) _ea = 5;
        }
    }

    public static int EG
    {
        get => _eg;
        set
        {
            _eg = value;
            if (_eg >= 5 && _ea >= 0) _eg = 0;
            if (_ea <= 4 && _eg > 9) _ea = 5;
        }
    }
}

一般说明:


应该避免使用
E
EA
EG
等名称!使用更多描述性名称!只要看一看名称,就应该知道什么是默认值以及值代表什么。这些速记迟早会给你带来麻烦的。如果您在6个月后查看代码,您将不再知道您头脑中的
EA
ment;)

您的Player.E从未初始化,因此我将其设置为Null。当您尝试访问Player上的任何内容时,这就是问题所在。它会为您提供NullReferenceExpection。在访问
Player.E
之前,您可能需要呼叫
Awake
。在提供评论/答案之前,请查看并确认。请注意,在Unity3d中控制对象创建的方式与在常规C#应用程序中控制对象创建的方式不同。我建议使用“E”之类的名称会导致以后出现问题。这是我修改后编译器错误所述的:“Assets/_Script/Player.cs(33,13):错误CS0200:无法为属性或索引器“Player.E”赋值--它是只读的”这不是单一行为您不能使用
new
进行
monobhavior
!!不MonoBehaviour@AhmedAli在OP的问题代码中,它说
公共类玩家:monobhavior
没问题。@austinzhangzzr背面注:如果你不显示代码,这实际上会回答问题(请注意,@austinzhangzzerthebeast实际上似乎对Unity3d单实例行为不感兴趣,而是标准的[singleton])。在
Awake
上创建第二个实例可以确保您拥有的静态实例与Unity3d实际实例化/使用的实例不同。。。只要“您需要为它提供一个实例:通过在Unity IDE界面(拖放)中添加一个对象或通过代码”就足以在后期工作中获得代码。
Player.E.EA = i;
public static class Player
{
    private static int _ea = 5;
    private static int _eg = 0;

    public static int EA
    {
        get => _ea;
        set
        {
            _ea = value;
            if (_eg >= 5 && _ea >= 0) _eg = 0;          
            if (_ea <= 4 && _eg > 9) _ea = 5;
        }
    }

    public static int EG
    {
        get => _eg;
        set
        {
            _eg = value;
            if (_eg >= 5 && _ea >= 0) _eg = 0;
            if (_ea <= 4 && _eg > 9) _ea = 5;
        }
    }
}
Player.EA = i;