• C# winform利用反射和自定义特性加载功能模块(插件式开发)


    由于在实际的工作中, 碰见这样的一个问题:

    一个软件, 销售给A客户 他需要所有功能,

    但是销售给B客户, 他只需要其中的一部分,

    1.如果我们在实际的开发过程中, 没有把一些功能模块区分开来的话, 那么带来的麻烦, 势必是要修改源代码。

    2.直到有一天,B客户又说需要某些功能,这个时候, 我们又要修改一次源代码, 更新给客户 , 所以想了想, 如果每个功能块都独立开来, 动态去加载功能, 这样就不用改动源代码, 客户需要哪些功能, 注册那些DLL给他们使用。

    ?.实现思路
    1.每个模块都用单独的程序集(DLL)分开  <反射动态加载>

    2.需要指定每个功能模块的命名空间       <特性标记命名空间

    !.实现代码

    1.程序中定义自定义特性, 用于映射程序的的模块信息和功能模块所在的命名空间

     /// <summary>
        /// 模块编号.
        /// </summary>
        public enum ModuleID
        {
            None = 0,
            DataDictionary = 1,
            SystemManage = 2
        }
    
        /// <summary>
        /// 模块名称.
        /// </summary>
        public class ModuleNames
        {
            public const string DataDictionary = "基础数据";
            public const string SystemManage = "系统管理";
        }
    
        /// <summary>
        /// 模块入口自定义特性
        /// </summary>
        public class AssemblyModuleEntry : Attribute
        {
    
            private ModuleID _moduleID;
            private string _moduleName;
            private string _moduleEntryNameSpace;
    
            /// <summary>
            /// 模块编号
            /// </summary>
            public ModuleID ModuleID { get { return _moduleID; } }
    
            /// <summary>
            /// 模块名称
            /// </summary>
            public string ModuleName { get { return _moduleName; } }
    
            /// <summary>
            /// 模块名字空间
            /// </summary>
            public string ModuleEntryNameSpace { get { return _moduleEntryNameSpace; } }
    
            /// <summary>
            /// 构造器
            /// </summary>
            /// <param name="moduleID">模块编号</param>
            /// <param name="moduleName">模块名称</param>
            /// <param name="moduleEntryNameSpace">模块名字空间</param>
            public AssemblyModuleEntry(ModuleID moduleID, string moduleName, string moduleEntryNameSpace)
            {
                _moduleID = moduleID;
                _moduleName = moduleName;
                _moduleEntryNameSpace = moduleEntryNameSpace;
            }
    
        }

    在模块的程序集设置好新建的自定义特性 (如下):

    // 有关程序集的一般信息由以下
    // 控制。更改这些特性值可修改
    // 与程序集关联的信息。
    [assembly: AssemblyModuleEntry(ModuleID.DataDictionary, ModuleNames.DataDictionary, "My.DataDictionary.Form1")]

    1.定义方法读取程序集(DLL)中的第一个特性信息

    /// <summary>
            /// 获取程序集自定义特性。是否用户自定义模块由AssemblyModuleEntry特性确定。
            /// </summary>
            public static AssemblyModuleEntry GetModuleEntry(Assembly asm)
            {
                AssemblyModuleEntry temp = new AssemblyModuleEntry(ModuleID.None, "", "");
                if (asm == null) return temp;
    
                object[] list = asm.GetCustomAttributes(typeof(AssemblyModuleEntry), false);
                if (list.Length > 0)
                    return (AssemblyModuleEntry)list[0];
                else
                    return temp;
            }

    2.保存AssemblyModuleEntry 的特性信息, 解析特性信息利用反射映射功能模块的主窗体

     /// <summary>
            /// 加载模块主方法
            /// </summary>
            /// <param name="moduleinfo">模块信息</param>
            /// <returns></returns>
            public virtual bool LoadModule(ModuleInfo moduleinfo)
            {
                _ModuleFileName = moduleinfo.ModuleFile;
                _ModuleAssembly = moduleinfo.ModuleAssembly;
                string entry = GetModuleEntryNameSpace(_ModuleAssembly);
                if (string.Empty == entry) return false;
    
                Form form = (Form)_ModuleAssembly.CreateInstance(entry);  //根据命名空间加载Form
                _ModuleMainForm = null;
    
                if (form is IModuleBase) _ModuleMainForm = (IModuleBase)form;
    
                return _ModuleMainForm != null;
            }

    3.将功能模块的功能都加载到全局缓存对象中, 创建首页的UI控件, 将缓存对象中的模块加载到首页中。

     

    实际效果图:

    注意: 反射是会带来性能的损耗, 但是经过合理的优化,还是对性能影响不大, 当个这个设计, 主要看每个人使用的取舍。

    核心思想:

    1.定义自定义特性

    2.将功能命名空间存储到自定义特性中(主要用于反射获取到指定的功能界面区)

    3.利用反射去获取目录下的有特性的程序集,加载指定的功能

    4.将反射获取的指定界面转换成缓存对象

    5.将缓存对象转换成首页指定的UI控件上。

    PS: 关于权限的控制思路:

    针对所登陆得用户, 获取当前用户所有的权限, 根据权限加载权限内的程序集。

    ---恢复内容结束---

  • 相关阅读:
    jstat命令行工具监控JVM内存和垃圾回收
    SkyWalking 日志监控
    SkyWalking 数据持久化
    问题记录: java 19000101 08:05:43 时间偏移bug
    springboot jest链接es
    redisRedisLockRegistry 分布式锁
    es 索引别名
    springboot elasticsearchresthighlevelclient 连接es
    缓存穿透、缓存击穿和缓存雪崩 概念
    Navicat for MySQL 导出中文乱码问题
  • 原文地址:https://www.cnblogs.com/zh7791/p/7910901.html
Copyright © 2020-2023  润新知