單實例使用探討

單例模式就是保證在整個應用程序的生命週期中,在任什麼時候刻,被指定的類只有一個實例,併爲客戶程序提供一個獲取該實例的全局訪問點。多線程

通常使用狀況:函數

        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;
            }
        }

這樣就能保證在多線程模式下,只能使用一個實例了。spa

可是這樣子不便於拓展,每次新增一個類就要多加一個單實例,很不方便,因而就想到了原先介紹使用的MEF,把每一個單實例都當作是一個插件使用,標記後,而後初始化的時候,實例化這些實例,放到相對應的容器中。對應用層只提供從容器中撈取的實例。插件

想法了而後就是實踐了:線程

1 定義單實例的Attributecode

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

 

2 單實例中初始化後,可能須要處理的事件,由於單實例做爲插件使用,通常都是使用無參的構造函數,進行反射實例,必然有些屬性沒法正常加載,必需要讀取配置,或者進行其餘處理,因此就要執行單實例類中初始化的事件,這樣就須要單獨標記MethodAttribute了以區分各種方法,所以第二件事定義MethodAttribute。orm

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

 

3 定義容器,存放各種單實例。blog

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實例了。

相關文章
相關標籤/搜索