[Unity实战]C#中的单例模式(附Unity测试样例)

401 阅读4分钟

设计模式:设计模式 | 菜鸟教程

单例模式:创建对象单例的动作转移到另外的行为上面, 利用一个行为去创建对象自身

个人推荐:

普通脚本-->V4方案

Unity脚本-->V10方案

using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;

/// <summary>
/// 单例模式:创建对象单例的动作转移到另外的行为上面, 利用一个行为去创建对象自身
/// 参考文献:Implementing the Singleton Pattern in C# https://csharpindepth.com/Articles/Singleton#performance
/// 个人推荐:普通脚本-->V4方案 Unity脚本-->V8方案
/// </summary>
public class SingletonTest : MonoBehaviour
{
    /// <summary>
    /// 测试代码
    /// </summary>
    private void Start()
    {
        //创建第一个线程并给予委托
        Thread thread1 = new Thread(new ThreadStart(thread1start));
        thread1.Start();
        //创建第二个线程并给予委托
        Thread thread2 = new Thread(new ThreadStart(thread2start));
        thread2.Start();
        // 泛型单例委托
        //Debug.Log(SingletonV7Test.GetInstance);
        // MonoBehaviour 单例委托
        //Debug.Log(SingletonTest.Instance);
    }

    static void thread1start()
    {
        Console.WriteLine("第一个线程运行");
        Debug.Log(SingletonV4.Instance);
    }

    static void thread2start()
    {
        Console.WriteLine("第二个线程运行");
        Debug.Log(SingletonV4.Instance);
    }

    private static SingletonTest instance;
    public static SingletonTest Instance
    {
        get
        {
            int length = FindObjectsOfType<SingletonTest>().Length;
            if (length == 0)
            {
                Debug.Log("单例已创建");
                GameObject go = new GameObject();
                go.name = typeof(SingletonTest).ToString();
                instance = go.AddComponent<SingletonTest>();
                return instance;
            }

            if (length > 1)
            {
                Debug.LogError("单例数量大于1");
                instance = null;
                return instance;
            }

            instance = FindObjectsOfType<SingletonTest>()[0];
            Debug.Log("单例已存在");
            return instance;
        }
    }
}

public sealed class SingletonV7Test : SingletonV7<SingletonV7Test>
{

}

// 第一个版本 ——不是线程安全的
public sealed class SingletonV1
{
    private static SingletonV1 instance = null;

    private SingletonV1()
    {
    }

    public static SingletonV1 Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new SingletonV1();
            }
            return instance;
        }
    }
}

// 第二个版本 —— 简单的线程安全
public sealed class SingletonV2
{
    private static SingletonV2 instance = null;
    private static readonly object padlock = new object();

    SingletonV2()
    {
    }

    public static SingletonV2 Instance
    {
        get
        {
            lock (padlock)
            {
                if (instance == null)
                {
                    instance = new SingletonV2();
                }
                return instance;
            }
        }
    }
}

// 第三个版本 - 使用双重检查锁定尝试线程安全
public sealed class SingletonV3
{
    private static SingletonV3 instance = null;
    private static readonly object padlock = new object();

    SingletonV3()
    {
    }

    public static SingletonV3 Instance
    {
        get
        {
            if (instance == null)
            {
                lock (padlock)
                {
                    if (instance == null)
                    {
                        instance = new SingletonV3();
                    }
                }
            }
            return instance;
        }
    }
}

// [推荐版本]第四个版本 - 不太懒,不使用锁且线程安全
public sealed class SingletonV4
{
    private static readonly SingletonV4 instance = new SingletonV4();

    // 显式静态构造函数告诉C#编译器
    // 不要将类型标记为BeforeFieldInit
    static SingletonV4()
    { }

    private SingletonV4()
    {
        Debug.Log("生成了SingletonV4单例");
    }

    public static SingletonV4 Instance
    {
        get
        {
            return instance;
        }
    }
}

// 第五版 - 完全懒惰的实例化
public sealed class SingletonV5
{
    private SingletonV5()
    {
    }

    public static SingletonV5 Instance { get { return Nested.instance; } }

    private class Nested
    {
        // 显式静态构造告诉C#编译器
        // 未标记类型BeforeFieldInit
        static Nested()
        {
        }

        internal static readonly SingletonV5 instance = new SingletonV5();
    }
}

// 第六版 - 使用.NET 4的 Lazy 类型(依赖.NET 4)
//public sealed class SingletonV6
//{
//    private static readonly Lazy<SingletonV6> lazy =
//        new Lazy<SingletonV6>(() => new SingletonV6());

//    public static Singleton Instance { get { return lazy.Value; } }

//    private SingletonV6()
//    {
//    }
//}

// 第七版 - 泛型单例问题==>外部泛型必须new() ==> 泛型无法控制new()
public class SingletonV7<T> where T : new()
{
    private static T instance;
    public static T GetInstance
    {
        get
        {
            if (instance == null)
            {
                instance = new T();
            }
            return instance;
        }
    }
}

// 第八版 - 雨松Momo推荐写法(Unity写法)
public class SingletonV8 : MonoBehaviour
{

    public static SingletonV8 instance;

    static SingletonV8()
    {
        GameObject go = new GameObject("#Globa#");
        DontDestroyOnLoad(go);
        instance = go.AddComponent<SingletonV8>();
    }

    public void DoSomeThings()
    {
        Debug.Log("DoSomeThings");
    }

    void Start()
    {
        Debug.Log("Start");
    }

    // 外部调用
    // MyScriptSingleton.instance.DoSomeThings();
}

// 第九版 - 公司中常见的单例模式(Unity写法)
public class SingletonV9 : MonoBehaviour
{
    public static SingletonV9 s;
    private void Awake()
    {
        s = this;
    }
}

// 第十版 - Unity单例泛型 用法
/// <summary>
/// public class GameManager : SingletonMono<GameManager>
///{
///
///    public override void Awake()
///{
///    base.Awake();
///}
///
///public void GameStart()
///{
///    Debug.Log("GameStart");
///}
///}
/// </summary>
/// <typeparam name="T"></typeparam>
public class SingletonV10<T> : MonoBehaviour where T : Component
{
    private static T instance;

    public static T Instance
    {
        get
        {
            if (instance == null)
            {
                instance = FindObjectOfType<T>();
                if (FindObjectsOfType<T>().Length > 1)
                {
                    Debug.LogError("instance count > 1");
                }
                else if (instance == null)
                {
                    GameObject obj = new GameObject(typeof(T).ToString());
                    instance = obj.AddComponent<T>();
                    Debug.Log("add instance");
                }
            }
            return instance;
        }
    }

    public virtual void Awake()
    {
        DontDestroyOnLoad(gameObject);
    }
}

[特别说明]:

SingletonTest这个类不能上生产,只用作简单测试,违背了Unity的启动线程的初衷

特殊单例(Sqlite):

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mono.Data.Sqlite;

//sqlManager类  数据
//做和sqlite的交互
//数据库的增删改查
//从数据库拿到数据的处理
public class SqliteManager
{
    #region 单例模式

    //私有化sqlManager对象
    private static SqliteManager instance;

    /// <summary>
    /// 对外提供给其他人调用这个唯一 对象的接口
    /// </summary>
    /// <returns>唯一 对象</returns>
    public static SqliteManager GetInstance()
    {
        if (instance == null)
        {
            instance = new SqliteManager();
        }
        return instance;
    }

    //无参构造方法 私有化  防止外部进行实例
    private SqliteManager()
    {

    }

    #endregion

    #region 数据库常用操作类的对象

    //数据库连接类
    private SqliteConnection con;
    //数据库指令类
    private SqliteCommand command;
    //数据库读取类
    private SqliteDataReader reader;

    #endregion

    #region 数据库常用方法封装类

    /// <summary>
    /// 打开数据库方法
    /// </summary>
    /// <param name="dataBaseName">数据库的名称</param>
    public void OpenDataBase(string dataBaseName)
    {
        try
        {
            //数据库名称的优化
            if (!dataBaseName.Contains(".sqlite"))
            {
                dataBaseName += ".sqlite";
            }
            //数据库路径拼接
            string dataPath = "Data Source=" + Application.streamingAssetsPath
                              + "/" + dataBaseName;
            //实例化连接对象 对sqlconnection进行赋值
            con = new SqliteConnection(dataPath);
            //对conmmad进行赋值
            command = con.CreateCommand();
            //打开连接
            con.Open();
        }
        catch (SqliteException e)
        {
            //打印错误日志
            Debug.LogError(e.Message);
        }
    }

    /// <summary>
    /// 关闭数据库
    /// </summary>
    public void CloseDataBase()
    {
        try
        {
            if (con != null)
            {
                con.Close();
            }
            if (reader != null)
            {
                reader.Close();
            }

        }
        catch (SqliteException e)
        {

            Debug.LogError(e.Message);
        }
    }

    /// <summary>
    /// 执行sql查询 增 删 改
    /// </summary>
    /// <param name="sqlString">执行增删改的sql语句</param>
    public void RunSql(string sqlString)
    {
        try
        {
            //对command对象的commandText进行赋值
            command.CommandText = sqlString;
            //执行commandText中的sql语句
            command.ExecuteNonQuery();
        }
        catch (SqliteException e)
        {
            Debug.LogError(e.Message);
        }
    }
    /// <summary>
    /// 查询单个数据
    /// </summary>
    /// <param name="sqlString">sql语句</param>
    /// <returns>查询结果</returns>
    public object SelectSingleData(string sqlString)
    {
        try
        {
            //对command的命令属性访问器进行赋值
            command.CommandText = sqlString;
            //进行单个查询操作
            object returnData = command.ExecuteScalar();
            //返回数据
            return returnData;
        }
        catch (SqliteException e)
        {
            Debug.LogError(e.Message);
            return null;
        }
    }
    /// <summary>
    /// 查询多个数据
    /// </summary>
    /// <param name="sqlString">sql指令</param>
    /// <returns>返回查询结果</returns>
    public List<ArrayList> SelectMultipleData(string sqlString)
    {
        //实例化list容器
        List<ArrayList> result = new List<ArrayList>();
        //对command对象的属性访问器  进行赋值
        command.CommandText = sqlString;
        //执行查询操作
        reader = command.ExecuteReader();

        while (reader.Read())
        {
            //每一档的数据信息
            ArrayList temp = new ArrayList();
            //当前档 所有信息的获取
            for (int i = 0; i < reader.FieldCount; i++)
            {
                //存储当前档的信息到 ArrayList中
                temp.Add(reader.GetValue(i));
            }
            //每一档信息加入List<ArrayList>
            result.Add(temp);
        }
        reader.Close();
        return result;
    }

    #endregion
}

参考文献:

Implementing the Singleton Pattern in C#

Implementing the Singleton Pattern in C#

雨松MOMO:

雨松MOMO程序研究院|专注移动互联网与Unity3D游戏开发的技术博客 — xuanyusong.com