Unity:静态引用在域重新加载(编辑器重新加载)后消失
Unity: Static reference disappears after domain reload (editor reload)
我有一个 C# 脚本可以归结为:
public class SingletonTest : MonoBehaviour
{
public static SingletonTest Singleton = null;
public int Value = 42;
void Start() {
Singleton = this;
}
}
起初运行良好,但我的问题是,当我编辑脚本然后单击回到 Unity editor/IDE 时,我得到一整串 NullReferenceException
行 SingletonTest.Singleton.Value
在其他一些 class.
我用谷歌搜索了一下,这个编辑器重新加载似乎触发了一个名为 Domain Reloading (see also 的过程。 显然,重新加载域还会重置所有静态字段,这解释了我的错误消息。我尝试了该页面上建议的一些解决方法,但 none 有效:
using UnityEngine;
public class SingletonTest : MonoBehaviour
{
public static SingletonTest Singleton = null;
public int Value = 42;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
static void StaticStart()
{
var arr = FindObjectsOfType<SingletonTest>();
Debug.Log($"Len arr: {arr.Length}"); // This is 0! :(
if (arr.Length > 0)
Singleton = arr[0];
}
void Start()
{
Singleton = this;
}
void OnAfterDeserialize()
{
Singleton = this;
}
}
我可以通过将 Singleton = this
放在 Update()
函数中来让它工作,但这个解决方案很难看,而且仍然在第一个给我一个 NullReferenceException
重新加载后的帧。
(请不要建议将 Value
字段设置为静态,这只是一个简化的示例,我 need/want 是一个单例 class)
当 CLR 创建 SingletonTest
对象时,考虑使用私有构造函数强制用一个值初始化静态字段。
尽管 Unity 通常不建议将构造函数与 MonoBehvior
一起使用,因为它们应该是 scripts(以及其他 Mono 和 Unity 怪癖)。我发现这非常适合我的单例用例(例如静态编辑器 Dictionary<T>
保存加载的元数据等...
public class SingletonTest : MonoBehaviour
{
public static SingletonTest Singleton { get; set; } = null;
public int Value = 42;
protected SingletonTest()
{
Singleton ??= this;
}
}
或者考虑避免假设给定的 field/property 永远不会为空。
例如:
void Awake()
{
// call method example ↓ (null-coalescing operator)
SingletonTest.Singleton?.Invoke("Something");
// property example
if(SingletonTest.Singleton != null)
{
Debug.Log($"{SingletonTest.Singleton.gameObject.Name}");
}
}
首先:到目前为止你还没有实现任何单例。
顾名思义,“单例”的主要概念是确保存在一个单一实例。在您的代码中,无法控制另一个实例是否会覆盖实例字段。
Singleton 被滥用后也只有 public 访问权限。
您的方式也很危险:任何其他脚本都可以简单地将字段设置为其他内容。
When/If 我这样做我通常会通过 属性 使用惰性初始化,例如
public class SingletonTest : MonoBehaviour
{
// Here you store the actual instance
private static SingletonTest _instance;
// Only allow read access for others
public static SingletonTest Instance
{
get
{
// If instance is assigned and exists return it right away
if(_instance) return _instance;
// Otherwise try to find one in the scene
_instance = FindObjectOfType<SingletonTest>();
// Found one? Nice return it
// this would only happen if this getter is called before the
// Awake had its chance to set the instance
if(_instance) return _instance;
// Otherwise create an instance now
// this will only happen if you forgot to put one into your scene
// it is not active/enabled
// or it was destroyed for some reason
_instance = new GameObject("SingletonTest").AddComponent<SingletonTest>();
return _instance;
}
}
public int Value = 42;
private void Awake()
{
// If another instance exists already then destroy this one
// This is the actual Singleton Pattern
if(_instance && _instance != this)
{
Destroy(gameObject);
return;
}
_instance = this;
// Optional: also make sure this instance survives any scene changes
DontDestroyOnLoad(gameObject);
}
}
然后如果你在编辑器中也需要这样的东西,而不是在PlayMode/runtime你可以使用[InitializeOnLoadMethod]
(NOT [RuntimeInitializeOnLoadMethod]
,顾名思义就是运行时/PlayMode).
当然你可以两者都喜欢
#if UNITY_EDITOR
[UnityEditor.InitializeOnLoadMethod]
#endif
[RuntimeInitializeOnLoadMethod]
private static void Init()
{
Debug.Log("Initialized", Instance);
}
对 Instance
属性 的简单访问已经确保它已被初始化。
我有一个 C# 脚本可以归结为:
public class SingletonTest : MonoBehaviour
{
public static SingletonTest Singleton = null;
public int Value = 42;
void Start() {
Singleton = this;
}
}
起初运行良好,但我的问题是,当我编辑脚本然后单击回到 Unity editor/IDE 时,我得到一整串 NullReferenceException
行 SingletonTest.Singleton.Value
在其他一些 class.
我用谷歌搜索了一下,这个编辑器重新加载似乎触发了一个名为 Domain Reloading (see also 的过程。 显然,重新加载域还会重置所有静态字段,这解释了我的错误消息。我尝试了该页面上建议的一些解决方法,但 none 有效:
using UnityEngine;
public class SingletonTest : MonoBehaviour
{
public static SingletonTest Singleton = null;
public int Value = 42;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
static void StaticStart()
{
var arr = FindObjectsOfType<SingletonTest>();
Debug.Log($"Len arr: {arr.Length}"); // This is 0! :(
if (arr.Length > 0)
Singleton = arr[0];
}
void Start()
{
Singleton = this;
}
void OnAfterDeserialize()
{
Singleton = this;
}
}
我可以通过将 Singleton = this
放在 Update()
函数中来让它工作,但这个解决方案很难看,而且仍然在第一个给我一个 NullReferenceException
重新加载后的帧。
(请不要建议将 Value
字段设置为静态,这只是一个简化的示例,我 need/want 是一个单例 class)
当 CLR 创建 SingletonTest
对象时,考虑使用私有构造函数强制用一个值初始化静态字段。
尽管 Unity 通常不建议将构造函数与 MonoBehvior
一起使用,因为它们应该是 scripts(以及其他 Mono 和 Unity 怪癖)。我发现这非常适合我的单例用例(例如静态编辑器 Dictionary<T>
保存加载的元数据等...
public class SingletonTest : MonoBehaviour
{
public static SingletonTest Singleton { get; set; } = null;
public int Value = 42;
protected SingletonTest()
{
Singleton ??= this;
}
}
或者考虑避免假设给定的 field/property 永远不会为空。
例如:
void Awake()
{
// call method example ↓ (null-coalescing operator)
SingletonTest.Singleton?.Invoke("Something");
// property example
if(SingletonTest.Singleton != null)
{
Debug.Log($"{SingletonTest.Singleton.gameObject.Name}");
}
}
首先:到目前为止你还没有实现任何单例。
顾名思义,“单例”的主要概念是确保存在一个单一实例。在您的代码中,无法控制另一个实例是否会覆盖实例字段。
Singleton 被滥用后也只有 public 访问权限。
您的方式也很危险:任何其他脚本都可以简单地将字段设置为其他内容。
When/If 我这样做我通常会通过 属性 使用惰性初始化,例如
public class SingletonTest : MonoBehaviour
{
// Here you store the actual instance
private static SingletonTest _instance;
// Only allow read access for others
public static SingletonTest Instance
{
get
{
// If instance is assigned and exists return it right away
if(_instance) return _instance;
// Otherwise try to find one in the scene
_instance = FindObjectOfType<SingletonTest>();
// Found one? Nice return it
// this would only happen if this getter is called before the
// Awake had its chance to set the instance
if(_instance) return _instance;
// Otherwise create an instance now
// this will only happen if you forgot to put one into your scene
// it is not active/enabled
// or it was destroyed for some reason
_instance = new GameObject("SingletonTest").AddComponent<SingletonTest>();
return _instance;
}
}
public int Value = 42;
private void Awake()
{
// If another instance exists already then destroy this one
// This is the actual Singleton Pattern
if(_instance && _instance != this)
{
Destroy(gameObject);
return;
}
_instance = this;
// Optional: also make sure this instance survives any scene changes
DontDestroyOnLoad(gameObject);
}
}
然后如果你在编辑器中也需要这样的东西,而不是在PlayMode/runtime你可以使用[InitializeOnLoadMethod]
(NOT [RuntimeInitializeOnLoadMethod]
,顾名思义就是运行时/PlayMode).
当然你可以两者都喜欢
#if UNITY_EDITOR
[UnityEditor.InitializeOnLoadMethod]
#endif
[RuntimeInitializeOnLoadMethod]
private static void Init()
{
Debug.Log("Initialized", Instance);
}
对 Instance
属性 的简单访问已经确保它已被初始化。