单实例使用探讨

单例模式就是保证在整个应用程序的生命周期中,在任何时刻,被指定的类只有一个实例,并为客户程序提供一个获取该实例的全局访问点。

一般使用情况:

        private static readonly object _lockObj = new object();
        private static TargetObject_instance;

        /// <summary>
        /// Get 唯一实例
        /// </summary>
        public static TargetObject Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (_lockObj)
                    {
                        if (_instance == null)
                            _instance = new TargetObject();
                    }
                }

                return _instance;
            }
        }

这样就能保证在多线程模式下,只能使用一个实例了。

但是这样子不便于拓展,每次新增一个类就要多加一个单实例,很不方便,于是就想到了原先介绍使用的MEF,把每个单实例都看成是一个插件使用,标记后,然后初始化的时候,实例化这些实例,放到相对应的容器中。对应用层只提供从容器中捞取的实例。

想法了然后就是实践了:

1 定义单实例的Attribute

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
    public class PartAttribute : Attribute
    {
    }

2 单实例中初始化后,可能需要处理的事件,因为单实例作为插件使用,一般都是使用无参的构造函数,进行反射实例,必然有些属性无法正常加载,必须要读取配置,或者进行其他处理,所以就要执行单实例类中初始化的事件,这样就需要单独标记MethodAttribute了以区分各类方法,因此第二件事定义MethodAttribute。

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
    public sealed class PartActivationMethodAttribute : Attribute
    {
    }

3 定义容器,存放各类单实例。

public sealed class PartContainer
    {
         internal IDictionary<Type, PartMetadata> PartInformation { get; set; }
         public void Initialize(IEnumerable<Assembly> partAssemblies)
        {
         }

        public void Activate()
        {
         }
    }

4定义初始化方法,查找已标记PartAttribute的Assembly

public void Initialize(IEnumerable<Assembly> partAssemblies)
        {
            var partInformation = from asm in partAssemblies
                                  from type in asm.GetTypes()
                                  where type.IsDefined(typeof(PartAttribute), false)
                                  select new
                                  {
                                      Type = type,
                                      Metadata = PartMetadata.GetPartMetadata(type)
                                  };
            PartInformation = partInformation.ToDictionary(p => p.Type, p => p.Metadata);

        }

5定义激活方法,调用已标记MethodAttribute的Method

public void Activate(IPartActivationContext configuration = null)
        {
            // 整理可以激活的插件类型
            var activePartsInfomation = new Dictionary<Type, PartMetadata>();
            foreach (var partInfo in PartInformation)
            {
                var type = partInfo.Key;
                var metadata = partInfo.Value;
                if (configuration == null || configuration.IsPartEnabled(type, metadata))
                {
                    activePartsInfomation[type] = metadata;
                }
            }

            // 激活插件
            ActiveParts.ForEach(p => OperateActivation<PartActivationMethodAttribute>(p));
        }

private void OperateActivation<OperationType>(object partInstance)
        {
                 // 根据Attribute找到方法
            var methodInfo = partInstance.GetType()
                .GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
                .Where(m => m.IsDefined(typeof(OperationType), false))
                .ToArray();

            if (methodInfo.Length == 0)
                return;

            if (methodInfo.Length != 1)
                throw new PartException(string.Format("重复的部件{0}方法", operationName));

            var activationMethod = methodInfo[0];
            var methodParameters = activationMethod.GetParameters();
            if (methodParameters != null && methodParameters.Length != 0)
                throw new PartException(string.Format("部件{0}方法不应该包含任何参数", operationName));
            methodInfo[0].Invoke(partInstance, null);
        }

然后就可以在目标实力类中使用了:

    [Part]
    internal class TargetObject : ITargetObject 
    {
         [PartActivationMethod]
        private void Initialize()
        {
        }
    }

使用顺序:

定义一个PartContainer partContainer

调用partContainer.Initialize(targetObjectAssemblie);

调用partContainer.Activate();

最后就能在partContainer.ActiveParts中获取自己想要的Targerobject实例了。

原文地址:https://www.cnblogs.com/gavinhuang/p/3548127.html