Unity中单例模式是非常常用的写法,可以基于C#语言的几种不同方法来实现。
下面我将列出几种常见的实现方式:
1. 经典的单例模式
csharp
public class SingletonExample : MonoBehaviour
{
private static SingletonExample instance;
public static SingletonExample Instance
{
get
{
if (instance == null)
{
instance = FindObjectOfType<SingletonExample>();
if (instance == null)
{
GameObject obj = new GameObject("SingletonExample");
instance = obj.AddComponent<SingletonExample>();
DontDestroyOnLoad(obj);
}
}
return instance;
}
}
private void Awake()
{
if (instance == null)
{
instance = this;
DontDestroyOnLoad(this.gameObject);
}
else if (instance != this)
{
Destroy(gameObject);
}
}
}
优点:
- 易于理解和实现。
- 可以在Unity的场景中直接使用,提供了与Unity生命周期事件的结合。
缺点:
- 不是完全的线程安全。在多线程环境下,可能会创建多个实例。
如果没有在场景中找到实例,它会创建一个新的GameObject,这可能会导致意外的副作用。
2. 使用静态构造函数
csharp
public class SingletonExample
{
private static readonly SingletonExample instance = new SingletonExample();
static SingletonExample()
{
}
private SingletonExample()
{
}
public static SingletonExample Instance
{
get
{
return instance;
}
}
}
优点:
- 实现简单,线程安全由CLR (公共语言运行时)保证。
- 静态构造函数只会被执行一次,保证了实例的唯一性。
缺点:
- 实例在程序运行时立即创建,无论是否使用,可能会导致资源的浪费。
不适合在Unity场景对象中使用,因为它与MonoBehaviour断开了联系,不能直接应用到GameObject上。
3. 使用Lazy类型确保线程安全
csharp
using System;
public class SingletonExample
{
private static readonly Lazy<SingletonExample> lazy =
new Lazy<SingletonExample>(() => new SingletonExample());
public static SingletonExample Instance { get { return lazy.Value; } }
private SingletonExample()
{
}
}
优点:
- 实现简单,线程安全由CLR (公共语言运行时)保证。
- 静态构造函数只会被执行一次,保证了实例的唯一性。
缺点:
- 实例在程序运行时立即创建,无论是否使用,可能会导致资源的浪费。
不适合在Unity场景对象中使用,因为它与MonoBehaviour断开了联系,不能直接应用到GameObject上。
4. 双重校验锁(Double-Check Locking)
csharp
public class SingletonExample
{
private static SingletonExample instance;
private static readonly object lockObject = new object();
public static SingletonExample Instance
{
get
{
if (instance == null)
{
lock (lockObject)
{
if (instance == null)
{
instance = new SingletonExample();
}
}
}
return instance;
}
}
private SingletonExample()
{
}
}
优点:
- 线程安全,并且在需要时才创建实例。
- 相对于Lazy,在早期的.NET版本中也可以使用。
缺点:
- 实现复杂,需要正确管理锁,否则可能会导致死锁。
- 性能开销,每次访问实例时都需要进行双重检查。
- 不直接与MonoBehaviour兼容,同样不适合直接应用于Unity场景中的对象。
总结:
在选择实现单例的方法时,应当考虑是否需要延迟初始化 、是否在多线程 环境中使用、以及是否需要与Unity的MonoBehaviour系统集成等因素。
在Unity中,经常使用第一种 方法,因为它能够更好地与Unity的组件和生命周期集成。
不过,如果你在Unity项目中需要使用单例模式管理非MonoBehaviour类型的资源或类,例如数据管理或服务类,那么**第三种方法(Lazy)**是一个非常好的选择。