单例模式就是保证在整个应用程序的生命周期中,在任何时刻,被指定的类只有一个实例,并为客户程序提供一个获取该实例的全局访问点。
一般使用情况:
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实例了。