c#通过反射实现单例模式

unity 专栏收录该内容
28 篇文章 1 订阅

常见的单例模式为:

不继承MONO:

public class Singleton{
    private static Singleton m_instance;

    public static Singleton Instance
    {
        get
        {
            if (m_instance == null)
            {
                m_instance = new Singleton();
            }

            return m_instance;
        }
    }

    private Singleton()
    {
     
    }
}

这里因为在unity中,对于线程安全那些就不考虑了。必要时加个锁(lock)。注意的两点是

1.防止外部再实例化这个类,要把构造函数设置为私有。

2.不继承MONO,可以直接通过new创建单例,如果继承MONO,上述方法无效,原因在于MONO是依附于GameObject的,不能通过new直接实例化继承MONO的类。

继承MONO:

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

public abstract class MonoSingleton<T> :MonoBehaviour  where T: MonoBehaviour
{
    private static T m_instance;

    public static T Instance
    {

        get
        {
            if (m_instance == null)
            {
                m_instance = Object.FindObjectOfType<T>();
                if (m_instance == null)
                {
                    GameObject obj=new GameObject("MonoSingleton");
                    m_instance = obj.AddComponent<T>();
                    Object.DontDestroyOnLoad(obj);
                }
            }

            return m_instance;
        }
    }
}

上面直接写了一个继承MONO的泛型类,任何继承MONO的单例直接引用这个就可以。下面重点介绍不继承MONO的单例写法,与最上面基本写法区别在于,不需要在每个单例中都写那么一大段单例,只需要继承该类即可。

泛型类:Singleton<T>

 public abstract class Singleton<T> where T : class, new()
    {

        protected static T _instance = null;

        public static T Instance
        {

            get
            {
                if (_instance == null)
                {
                    _instance = new T();
                }

                return _instance;
            }
        }
       
        protected Singleton()
        {
          Init();
        }

        public virtual void Init()
        {

        }
    }

这种通过泛型的方式好处在于继承它的类可以全部以单例去实现,不需要单独再写。但是!!要注意两点:

1.必须加上new(),这样才能对它进行实例化,不然会报下列错误

2.虽然这样可以省掉每个类中单独去申明变量,但也导致将类的构造函数暴露了出来。

比如这里你新建了一个类继承它,那构造函数就无法设置成private,这样就导致单例的不合理性。

至此,上面的问题暴露出来的问题是
如果每个单例类单独写Instance变量,虽然可以实现构造方法私有,但比较繁琐。

如果统一通过泛型类去做单例,虽然书写简单了很多,但是切导致构造函数暴露。

这也印出来本文的重头戏:用反射,解决以上两个问题,实现单例的效果

通过反射实现单例:

  public abstract class Singleton<T> where T : class
    {
        private static T m_intance;

        public static T Instance
        {
            get
            {
                if (null == m_intance)
                {
                    m_intance = null;
                    Type type = typeof (T);
                    System.Reflection.ConstructorInfo[] constructorInfoArray =
                        type.GetConstructors(System.Reflection.BindingFlags.Instance |
                                             System.Reflection.BindingFlags.NonPublic);

                    foreach (System.Reflection.ConstructorInfo constructorInfo in constructorInfoArray)
                    {
                        System.Reflection.ParameterInfo[] parameterInfoArray = constructorInfo.GetParameters();
                        if (0 == parameterInfoArray.Length)
                        {
                            m_intance = (T) constructorInfo.Invoke(null);
                            break;
                        }
                    }

                    if (null == m_intance)
                    {
                        throw new NotSupportedException("No NonPublic constructor without 0 parameter");
                    }
                }

                return m_intance;
            }
        }

        protected Singleton()
        {
        }

        public static void Destroy()
        {
            m_intance = null;
        }

    }

简单解释一下反射的原理,首先将类型强转,Type type = typeof (T);然后获取到它的构造函数的类型和参数信息。这里做了一个监测,如果构造函数是私有或者静态,并且构造函数无参,才会进行单例的实现。

所以这里用反射的原因就在于,可以通过反射去调用私有的构造函数,实现了构造函数不对外暴露的同时,实现了单例的简化,不需要每个单例类单独书写。

唯一的缺点,应该就是这种方式比较复杂,稍微多了一点空间上的内存和一丁点性能,可以忽略不计。这里引用一个别人对于反射的博客:

https://www.jianshu.com/p/2f0cfdf116c8

至此,单例的几种模式就介绍完了,个人喜欢直接把反射方式做成一个公共库中的类,每个项目直接调用就OK。有什么不懂和意见,欢迎留言咨询。

  • 0
    点赞
  • 0
    评论
  • 2
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

参与评论 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

大苏苏说

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值