用C#语言泛化单件模式

本人对于C#只能算是初学者,写此随笔,也只为和大家一起讨论,共同进步。

 

我看到过一些用C#语言泛化单件(Singleton)的文章和代码,里面有个问题,即用new来创建对象,比如类似下面这段代码:

 

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

 

要想让其工作,你必须让T的构造函数为public。但是,如果T的构造函数为public,那任何客户代码都可调用new T()。你无法用语言本身限制其只能被创建最多一次和只有一个全局访问点,这样也就无法保证你的单件成为“单件”。

 

C++里有友元(friend),T的构造函数可是设为non-public,然后用单件的模板类作为友元去访问它。用ACE的单件模板类ACE_Singleton时,就是如此实现。但C#没有友元。

 

CodeProject有篇文章:Generic Singleton Pattern using Reflection, in C#。作者用.Net反射机制实现了泛化的单件,重要的是它可以用具有non-public构造函数的public类来实例化。他用Type.GetConstructor来找到non-public的构造函数,并调用它来创建实例。他的代码如下(我去掉了异常处理部分):

 

ConstructorInfo constructor = typeof(T).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, nullnew Type[0], null);
instance = (T)constructor.Invoke(null);

 

我仿照它写了我自己的泛化Singleton,见下:

 

    public static class Singleton<T> where T : class
    {
        private static volatile T _instance;
        private static object _lock = new object();

        public static T Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (_lock)
                    {
                        if (_instance == null)
                        {
                            Type type = typeof(T);
                            ConstructorInfo ctor;
                            ctor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
                                          nullnew Type[0], new ParameterModifier[0]);
                            _instance = (T)ctor.Invoke(new object[0]);
                        }
                    }
                }

                return _instance;
            }
        }
    }

 

当使用它时,客户代码是这样的:

 

    public sealed class ZoomTool : BaseTool
    {
        #region Constructor

        private ZoomTool()
        {
        }

        #endregion

        #region Properties

        ......

        public static ZoomTool Instance
        {
            get { return Singleton<ZoomTool>.Instance; }
        }

        #endregion
    }

 

使用ZoomTool单件时代码是这样:

 

_mapToolTypeToTool.Add(ToolType.Zoom, ZoomTool.Instance); 

 

但是这种泛型单件有一个问题:我得到了Code Analysis的CA1000警告:“不要在泛型类型中声明静态成员(Do not declare static members on generic types)”。

 

于是我将泛型类Singleton<T>改为普通类,将其中的Instance函数改为GetInstance<T>泛型函数,写下了另一个泛化的单件。

 

    public static class Singleton
    {
        private static class Storage<T> where T : class
        {
            internal static volatile T _instance;
        }

        private static object _lock = new object();

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design""CA1004:GenericMethodsShouldProvideTypeParameter")]
        public static T GetInstance<T>() where T : class
        {
            if (Storage<T>._instance == null)
            {
                lock (_lock)
                {
                    if (Storage<T>._instance == null)
                    {
                        Type type = typeof(T);
                        ConstructorInfo ctor;
                        ctor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
                                      nullnew Type[0], new ParameterModifier[0]);
                        Storage<T>._instance = (T)ctor.Invoke(new object[0]);
                    }
                }
            }
            return Storage<T>._instance;
        }
    }
}

 

这时客户代码是这样的:

 

    public sealed class ZoomTool : BaseTool
    {
        #region Constructor

        private ZoomTool()
        {
        }

        #endregion

        #region Properties

        ......

        public static ZoomTool Instance
        {
            get { return Singleton.GetInstance<ZoomTool>(); }
        }

        #endregion
    }

 

但我得到了另一个Code Analysis警告CA1004:“泛型方法应提供类型参数(Generic methods should provide type parameter)”。

 

MSDN里不推荐压制这两个消息(SuppressMessage),我感觉是泛型技术里有些规则限制的。我不太明白为什么要提示这2个警告。希望有经验的人,能说说自己的理解。先谢过了!

 

posted @ 2012-07-06 10:05  不弃的追求  阅读(273)  评论(0编辑  收藏  举报