在统一中使用单例的最佳方法



我想知道哪种是使用单例实例的正确方法:当我创建一个名为"Manager"的单例类并且它包含一个名为"value"的整数变量并且我有另一个名为"A"的类时......

//singleton class
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Manager : MonoBehaviour {
public static Manager Instance{ set; get;}
public int value;
private void Awake () {
if (Instance == null) {
Instance = this;
DontDestroyOnLoad (this.gameObject);
} else {
Destroy (gameObject);
}
}
}

所以在我的 A 类中,我可以像这样创建一个单例实例: 示例伪代码 公共类 A {

// First
// using as a global variable
Manager manager;
//------------------------------------
Public void tstOne(){
// First
// using that global variable
manager.Instance.value = 0;
// Second
// or this way
Manager.Instance.value = 0; 
}
Public void tstTwo(){
// First
// using that global variabl
manager.Instance.value = 1;
// Second
// or this way
Manager.Instance.value = 1; 
}
}

所以我的问题是 - 我应该创建一个全局实例,然后像第一个实例一样使用该实例,还是应该使用第二个示例?

它们在内存消耗和效率方面是否相同? 还是有另一种使用单例的方法?

我确认 Ruzihm 的回答(我也同意 derHugo 的评论,我个人更喜欢 SO 并在需要时引用它们。比单例或 DI 直接方法干净得多)。

具体而言,访问实例成员manager.Instance.value速度较慢,并且还需要在堆上进一步分配内存,因此会损害内存使用和速度性能。(非常小的性能问题,但仍然如此。

单例还有进一步的改进空间,特别是如果你不需要它从MonoBehavior派生

。您可以:

  1. 如果您忘记在场景中添加它,请确保它也已构建
  2. 使其线程安全(通常不是 Unity 需要的)
  3. 懒惰地实现它(意味着您只在需要时创建它)
  4. 此外,作为一般规则,最好密封单例类以提高性能(当您使用虚拟方法时,再次确实略有改进)

这将是实现(考虑到您将Manager用作帮助程序实例,如果您需要单行为逻辑,则YourManagerMono

public class YourManagerMono : MonoBehaviour
{
}
public sealed class Manager
{
private static readonly Manager instance = new Manager();
//use a gameobject to relate it in the scene
private YourManagerMono monoManager;
public static Manager Instance => instance;
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Manager() {}
private Manager()
{
//find or create a manager
monoManager = GameObject.FindWithTag("Manager").GetComponent<YourManagerMono>();
if (monoManager == null)
{
monoManager = new GameObject().AddComponent<YourManagerMono>();
monoManager.gameObject.tag = "Manager";
Object.DontDestroyOnLoad(monoManager.gameObject);
}
}
}

Jon Skeet 撰写的一篇关于 C# 单例实现的好文章(我使用了实现 4)。

编辑:
我再次同意德雨果(在这个答案的新评论中)。我的例子用于展示一个有趣的前景并提供尽可能多的性能,但如果你只需要一个只有第 1 点和第 3 点的 Monobehavior,你可以继续使用来自 Unify 社区的单例通用实现(只需记住将结束类设置为密封以帮助编译器)。

Instance

是一个静态字段,因此您应该使用Manager.Instance获取对单例的引用。

Manager.Instance是全局可访问的实例,因为它是公共静态的。C# 中没有全局变量这样的东西。

直接通过类名访问静态字段肯定比将第二个实例作为 A 中的字段,然后使用它访问静态实例要好。

对于只想存在于一个场景中的单身人士,我使用:

public class SceneSingleton<T> : MonoBehaviour
where T : SceneSingleton<T>
{
static T s_instance = null;
public static T Instance
{
get
{
if (s_instance == null)
{
s_instance = Object.FindObjectOfType<T>();
}
return s_instance;
}
}
}

然后从中继承你的类,如下所示:

public class MyManager : SceneSingleton<MyManager>

如果您需要您的单例在所有场景之间保持活动状态,请尝试使用以下命令:

public class GlobalSingleton<T> : MonoBehaviour
where T : GlobalSingleton<T>
{
static T s_instance = null;
public static T Instance
{
get
{
if (s_instance == null)
{
GameObject prefab = Resources.Load(typeof(T).Name) as GameObject;
if (prefab == null || prefab.GetComponent<T>() == null)
{
Debug.LogError("Prefab for game manager " + typeof(T).Name + " is not found");
}
else
{
GameObject gameManagers = GameObject.Find("GameManagers");
if (gameManagers == null)
{
gameManagers = new GameObject("GameManagers");
DontDestroyOnLoad(gameManagers);
}
GameObject gameObject = Instantiate(prefab);
gameObject.transform.parent = gameManagers.transform;
s_instance = gameObject.GetComponent<T>();
s_instance.Init();
}
}
return s_instance;
}
}
protected virtual void Init()
{ }
}

最新更新