C# 使用单行为构造函数的Unity3D单例
我有几个C# 使用单行为构造函数的Unity3D单例,c#,unity3d,singleton,C#,Unity3d,Singleton,我有几个monobhavior子类需要成为Singleton,但是在Awake()中分配实例属性对于某些类来说太晚了,并且会导致竞争条件,所以我想知道在私有c-tor中是否有任何东西反对分配实例,像这样: public class Foo: MonoBehaviour { public static Foo Instance { get; private set; } private Foo() { Instance = this; } } p
monobhavior
子类需要成为Singleton
,但是在Awake()
中分配实例
属性对于某些类来说太晚了,并且会导致竞争条件,所以我想知道在私有c-tor中是否有任何东西反对分配实例
,像这样:
public class Foo: MonoBehaviour
{
public static Foo Instance { get; private set; }
private Foo()
{
Instance = this;
}
}
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;
}
}
或者这种方法是否有任何我需要注意的负面影响?您使用的技术允许多次设置实例属性,即使只是由同一类的其他成员设置。这是不可能的。我会这样做:
public class Foo: MonoBehaviour
{
public static Foo Instance { get; private set; }
private Foo()
{
Instance = this;
}
}
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;
}
}
这是一种确保singleton变量只设置一次的简单方法。如果需要惰性实例化,可以执行以下操作:
public class Foo: MonoBehaviour
{
public static Foo Instance { get; private set; }
private Foo()
{
Instance = this;
}
}
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;
}
}
但是使用这种技术,您仍然可以多次写入实例变量。因此,您需要确保始终引用属性而不是变量。如果要从多个线程访问此属性,则需要使用以下关键部分来防止争用情况:
public class Foo: MonoBehaviour
{
public static Foo Instance { get; private set; }
private Foo()
{
Instance = this;
}
}
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;
}
}
这是双重检查锁定模式。如果代码访问不多和/或性能不是问题,则可以使用常规锁定。您使用的技术允许多次设置实例属性,即使只有同一类的其他成员可以设置实例属性。这是不可能的。我会这样做:
public class Foo: MonoBehaviour
{
public static Foo Instance { get; private set; }
private Foo()
{
Instance = this;
}
}
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;
}
}
这是一种确保singleton变量只设置一次的简单方法。如果需要惰性实例化,可以执行以下操作:
public class Foo: MonoBehaviour
{
public static Foo Instance { get; private set; }
private Foo()
{
Instance = this;
}
}
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;
}
}
但是使用这种技术,您仍然可以多次写入实例变量。因此,您需要确保始终引用属性而不是变量。如果要从多个线程访问此属性,则需要使用以下关键部分来防止争用情况:
public class Foo: MonoBehaviour
{
public static Foo Instance { get; private set; }
private Foo()
{
Instance = this;
}
}
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;
}
}
这是双重检查锁定模式。如果代码访问不多和/或性能不是问题,可以使用常规锁定。我同意Lorek的答案,但有一个问题 您不应该使用MonoBehavior的构造函数,因为它本身有不需要的行为。因为它不是特定游戏对象的一部分。因此,您必须将其添加到该行为的Init、Awake或Start方法中;或者创建一个新类来包含要共享的逻辑。(新类不应由MonoBehavior类扩展) 然后按照上面Lorek的描述创建一个单例 您还可以更改脚本执行顺序,以确保需要作为“单例”运行的MonoBehavior在所有其他脚本之前执行。 但是,必须已经将此MonoBehavior附加到场景中的现有游戏对象,而不是自动/通过代码添加
我同意洛雷克的回答,但有一个问题 您不应该使用MonoBehavior的构造函数,因为它本身有不需要的行为。因为它不是特定游戏对象的一部分。因此,您必须将其添加到该行为的Init、Awake或Start方法中;或者创建一个新类来包含要共享的逻辑。(新类不应由MonoBehavior类扩展) 然后按照上面Lorek的描述创建一个单例 您还可以更改脚本执行顺序,以确保需要作为“单例”运行的MonoBehavior在所有其他脚本之前执行。 但是,必须已经将此MonoBehavior附加到场景中的现有游戏对象,而不是自动/通过代码添加
谢谢大家的支持!我最终提出了以下方法,因为在我的例子中,这些单例是通过编辑器(从菜单)创建的,并且单例应该是容器游戏对象上的组件
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>();
}
publicstatict GetInstance(stringcontainerName),其中T:Component
{
/*查找容器或创建(如果不存在)*/
var container=GameObject.Find(containerName);
如果(container==null)container=newgameobject(containerName);
/*获取现有实例,如果找不到,则创建新实例*/
返回container.GetComponent()??container.AddComponent();
}
当然,它也不是完美的,因为它只依赖于对象名。但是它对我很有用。谢谢大家的意见!我最终提出了以下方法,因为在我的例子中,这些单例是通过编辑器(从菜单)创建的,并且单例应该是容器游戏对象上的组件
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>();
}
publicstatict GetInstance(stringcontainerName),其中T:Component
{
/*查找容器或创建(如果不存在)*/
var container=GameObject.Find(containerName);
如果(container==null)container=newgameobject(containerName);
/*获取现有实例,如果找不到,则创建新实例*/
返回container.GetComponent()??container.AddComponent();
}
当然,它也不是完美的,因为它只依赖于对象名。但是它对我很有用。如果您需要从任何地方访问单个全局MonoBehavior脚本,MonoSingleton类非常有用。以下是MonoSingleton类:
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)
}
}
}
}
}
}
如果您需要从任何地方访问单个全局MonoBehavior脚本,则MonoSingleton类非常有用。以下是MonoSingleton类:
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)
}
}
}
}
}
}
好的,到目前为止我基本上就是这样做的。但我不确定自己是否完全理解Unity如何实例化组件。它们不是由AddComponent()实例化的吗?至少看起来是这样。在这种情况下,“手动”实例化会导致另一个实例,不是吗?因为GetComponent()肯定不使用实例getter。我明白你现在的问题了。我去拿b