使用 MonoBehavior 的构造函数的 Unity3D Singleton
Unity3D Singleton with using Constructor of MonoBehavior
我有几个 MonoBehavior
sub类 需要是 Singleton
但是在 Awake()
中分配 Instance
属性 也是迟到一些 类 并导致竞争条件,所以我想知道是否有任何反对在私有 c-tor 中分配 Instance
的东西,如下所示:
public class Foo: MonoBehaviour
{
public static Foo Instance { get; private set; }
private Foo()
{
Instance = this;
}
}
或者我需要注意这种方法是否有任何负面影响?
您使用的技术允许实例 属性 设置多次,即使仅由同一 class 的其他成员设置也是如此。这是一个禁忌。我会做这样的事情:
private static readonly Foo instance = new Foo();
public static Foo Instance
{
get
{
return instance;
}
}
这是确保单例变量仅设置一次的简单方法。如果你想要惰性实例化,你可以这样做:
private static Foo instance = null;
public static Foo Instance
{
get
{
if (null == instance)
instance = new Foo();
return instance;
}
}
但是使用这种技术,您仍然可以多次写入您的实例变量。因此,您需要确保始终引用 属性 而不是变量。如果要从多个线程访问此 属性,您需要使用如下关键部分来防止竞争条件:
private static readonly object singletonSection = new object();
private static Foo instance = null;
public static Foo Instance
{
get
{
if (null == instance)
{
lock(singletonSection)
{
if (null == instance)
instance = new Foo();
}
}
return instance;
}
}
这是双重检查锁定模式。如果代码访问不多,您可以使用常规锁定 and/or 性能不是问题。
我同意 Lorek 的回答,但有一个问题。
您不应使用 MonoBehavior 的构造函数,因为它本身具有不需要的行为。因为它不会成为特定游戏对象的一部分。因此,您必须将其添加到该行为的 Init、Awake 或 Start 方法中;或者创建一个新的 class 来包含您要共享的逻辑。 (新 class 不应由 MonoBehavior class 扩展)
然后按照 Lorek 上面的描述创建一个单例。
您还可以更改脚本执行顺序,以确保需要作为 "singleton" 工作的 MonoBehavior 在所有其他脚本之前执行。
但是,有必要将此 MonoBehavior 附加到场景中现有的游戏对象,而不是添加 automatically/by 代码。
感谢任何人的输入!我最终想到了以下方法,因为在我的例子中,这些单例是通过编辑器(从菜单)创建的,单例应该是容器游戏对象上的组件。
public static T GetInstance<T>(string containerName) where T : Component
{
/* Find container or create if it doesn't exist. */
var container = GameObject.Find(containerName);
if (container == null) container = new GameObject(containerName);
/* Get existing instance or create new one if not found. */
return container.GetComponent<T>() ?? container.AddComponent<T>();
}
当然它也不完美,因为它只依赖于对象名称。但它对我有用。
MonoSingleton class 如果您需要从任何地方访问单个全局 MonoBehaviour 脚本,那么它很有用。这是 MonoSingleton class:
using UnityEngine;
public class MonoSingleton<T> where T : MonoBehaviour
{
private static T _instance;
private static bool isFound;
private bool createMissingInstance;
static MonoSingleton()
{
isFound = false;
_instance = null;
}
public MonoSingleton(bool createNewInstanceIfNeeded = true)
{
this.createMissingInstance = createNewInstanceIfNeeded;
}
public T Instance
{
get
{
if (isFound && _instance)
{
return _instance;
}
else
{
UnityEngine.Object[] objects = GameObject.FindObjectsOfType(typeof(T));
if (objects.Length > 0)
{
if (objects.Length > 1)
Debug.LogWarning(objects.Length + " " + typeof(T).Name + "s were found! Make sure to have only one at a time!");
isFound = true;
_instance = (T) System.Convert.ChangeType(objects[0], typeof(T));
return _instance;
}
else
{
Debug.LogError(typeof(T).Name + " script cannot be found in the scene!!!");
if (createMissingInstance)
{
GameObject newInstance = new GameObject(typeof(T).Name);
isFound = true;
_instance = newInstance.AddComponent<T>();
Debug.Log(typeof(T).Name + " was added to the root of the scene");
return _instance;
}
else
{
isFound = false;
return null; // or default(T)
}
}
}
}
}
}
然后在静态 class 中定义 MonoSingletons。例如:
public static class Global
{
public static MonoSingleton<GameProgress> progress = new MonoSingleton<GameProgress>(true); //true means that new GameObject with script GameProgress will be created if it is missing from the scene
public static MonoSingleton<CharacterController> characterController = new MonoSingleton<CharacterController>(false); //will return null if there is no character controller present in the scene.
}
然后只需从任何脚本访问全局 MonoBehaviors!
Global.progress.Instance.score++;
我有几个 MonoBehavior
sub类 需要是 Singleton
但是在 Awake()
中分配 Instance
属性 也是迟到一些 类 并导致竞争条件,所以我想知道是否有任何反对在私有 c-tor 中分配 Instance
的东西,如下所示:
public class Foo: MonoBehaviour
{
public static Foo Instance { get; private set; }
private Foo()
{
Instance = this;
}
}
或者我需要注意这种方法是否有任何负面影响?
您使用的技术允许实例 属性 设置多次,即使仅由同一 class 的其他成员设置也是如此。这是一个禁忌。我会做这样的事情:
private static readonly Foo instance = new Foo();
public static Foo Instance
{
get
{
return instance;
}
}
这是确保单例变量仅设置一次的简单方法。如果你想要惰性实例化,你可以这样做:
private static Foo instance = null;
public static Foo Instance
{
get
{
if (null == instance)
instance = new Foo();
return instance;
}
}
但是使用这种技术,您仍然可以多次写入您的实例变量。因此,您需要确保始终引用 属性 而不是变量。如果要从多个线程访问此 属性,您需要使用如下关键部分来防止竞争条件:
private static readonly object singletonSection = new object();
private static Foo instance = null;
public static Foo Instance
{
get
{
if (null == instance)
{
lock(singletonSection)
{
if (null == instance)
instance = new Foo();
}
}
return instance;
}
}
这是双重检查锁定模式。如果代码访问不多,您可以使用常规锁定 and/or 性能不是问题。
我同意 Lorek 的回答,但有一个问题。
您不应使用 MonoBehavior 的构造函数,因为它本身具有不需要的行为。因为它不会成为特定游戏对象的一部分。因此,您必须将其添加到该行为的 Init、Awake 或 Start 方法中;或者创建一个新的 class 来包含您要共享的逻辑。 (新 class 不应由 MonoBehavior class 扩展)
然后按照 Lorek 上面的描述创建一个单例。
您还可以更改脚本执行顺序,以确保需要作为 "singleton" 工作的 MonoBehavior 在所有其他脚本之前执行。 但是,有必要将此 MonoBehavior 附加到场景中现有的游戏对象,而不是添加 automatically/by 代码。
感谢任何人的输入!我最终想到了以下方法,因为在我的例子中,这些单例是通过编辑器(从菜单)创建的,单例应该是容器游戏对象上的组件。
public static T GetInstance<T>(string containerName) where T : Component
{
/* Find container or create if it doesn't exist. */
var container = GameObject.Find(containerName);
if (container == null) container = new GameObject(containerName);
/* Get existing instance or create new one if not found. */
return container.GetComponent<T>() ?? container.AddComponent<T>();
}
当然它也不完美,因为它只依赖于对象名称。但它对我有用。
MonoSingleton class 如果您需要从任何地方访问单个全局 MonoBehaviour 脚本,那么它很有用。这是 MonoSingleton class:
using UnityEngine;
public class MonoSingleton<T> where T : MonoBehaviour
{
private static T _instance;
private static bool isFound;
private bool createMissingInstance;
static MonoSingleton()
{
isFound = false;
_instance = null;
}
public MonoSingleton(bool createNewInstanceIfNeeded = true)
{
this.createMissingInstance = createNewInstanceIfNeeded;
}
public T Instance
{
get
{
if (isFound && _instance)
{
return _instance;
}
else
{
UnityEngine.Object[] objects = GameObject.FindObjectsOfType(typeof(T));
if (objects.Length > 0)
{
if (objects.Length > 1)
Debug.LogWarning(objects.Length + " " + typeof(T).Name + "s were found! Make sure to have only one at a time!");
isFound = true;
_instance = (T) System.Convert.ChangeType(objects[0], typeof(T));
return _instance;
}
else
{
Debug.LogError(typeof(T).Name + " script cannot be found in the scene!!!");
if (createMissingInstance)
{
GameObject newInstance = new GameObject(typeof(T).Name);
isFound = true;
_instance = newInstance.AddComponent<T>();
Debug.Log(typeof(T).Name + " was added to the root of the scene");
return _instance;
}
else
{
isFound = false;
return null; // or default(T)
}
}
}
}
}
}
然后在静态 class 中定义 MonoSingletons。例如:
public static class Global
{
public static MonoSingleton<GameProgress> progress = new MonoSingleton<GameProgress>(true); //true means that new GameObject with script GameProgress will be created if it is missing from the scene
public static MonoSingleton<CharacterController> characterController = new MonoSingleton<CharacterController>(false); //will return null if there is no character controller present in the scene.
}
然后只需从任何脚本访问全局 MonoBehaviors!
Global.progress.Instance.score++;