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

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

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

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

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

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

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

!.实现代码

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

 /// <summary>
/// 模块编号.
/// </summary>
public enum ModuleID
{
None = ,
DataDictionary = ,
SystemManage =
} /// <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 > )
return (AssemblyModuleEntry)list[];
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: 关于权限的控制思路:

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

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

C# winform利用反射和自定义特性加载功能模块(插件式开发)的更多相关文章

  1. 利用反射跟自定义注解拼接实体对象的查询SQL

    前言 项目中虽然有ORM映射框架来帮我们拼写SQL,简化开发过程,降低开发难度.但难免会出现需要自己拼写SQL的情况,这里分享一个利用反射跟自定义注解拼接实体对象的查询SQL的方法. 代码 自定义注解 ...

  2. 【译】12. Java反射——类的动态加载和重新加载

    原文地址:http://tutorials.jenkov.com/java-reflection/dynamic-class-loading-reloading.html 博主最近比较忙,争取每周翻译 ...

  3. Django 修改视图文件(views.py)并加载Django模块 + 利用render_to_response()简化加载模块 +locals()

    修改视图代码,让它使用 Django 模板加载功能而不是对模板路径硬编码.返回 current_datetime 视图,进行如下修改: from django.template.loader impo ...

  4. Flex 4 自定义预加载器

    本示例的目的是在Flash Professional里创建自定义预加载器SWC,并扩展SparkDownloadProgressBar类在Flex 4应用程序中使用.    预加载器显示加载进度百分比 ...

  5. 【SpringBoot 基础系列】实现一个自定义配置加载器(应用篇)

    [SpringBoot 基础系列]实现一个自定义配置加载器(应用篇) Spring 中提供了@Value注解,用来绑定配置,可以实现从配置文件中,读取对应的配置并赋值给成员变量:某些时候,我们的配置可 ...

  6. PHP自动加载功能原理解析

    前言 这篇文章是对PHP自动加载功能的一个总结,内容涉及PHP的自动加载功能.PHP的命名空间.PHP的PSR0与PSR4标准等内容. 一.PHP自动加载功能 PHP自动加载功能的由来 在PHP开发过 ...

  7. 也来学学插件式开发续-利用MEF

    前面一个博客:也来学学插件式开发中很多朋友留言说可以用MEF来实现.于是我就试着用MEF实现了一下. 步骤和上一篇差不多,只是加载插件的方式有所不同.这只是一个自己的示例程序,肯定有很多不足之处,欢迎 ...

  8. Angular - 预加载 Angular 模块

    Angular - 预加载延迟模块 在使用路由延迟加载中,我们介绍了如何使用模块来拆分应用,在访问到这个模块的时候, Angular 加载这个模块.但这需要一点时间.在用户第一次点击的时候,会有一点延 ...

  9. RecyclerViewLoadMoreDemo【封装上拉加载功能的RecyclerView,搭配SwipeRefreshLayout实现下拉刷新】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 封装含有上拉加载功能的RecyclerView,然后搭配SwipeRefreshLayout实现下拉刷新.上拉加载功能. 在项目中将 ...

随机推荐

  1. leetcode -day23 Construct Binary Tree from Inorder and Postorder Traversal &amp; Construct Binary Tree f

    1.  Construct Binary Tree from Inorder and Postorder Traversal Given inorder and postorder travers ...

  2. vue22 路由

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. 弯道超车,换一个思路,避免addEventListener为同一个元素重复赋予事件

    addEventListener可以给同一个元素赋予多次同一个事件. 执行一次,就多一次事件效果.这不是我想要的. window.onload = function(){ var box = docu ...

  4. 简单日志LogHelper

    public static class LogHelper { //日志存储路径 private static string LogPath = Path.Combine(AppDomain.Curr ...

  5. 关于在天机项目中遇到的常用git 命令

    1. 本地分支和远程分支 1>我们在本地创建分支,第一次push到远程是没有分支存在,执行git push 会有提示,按照提示的内容操作即可,当然我们也可以 git push origin fe ...

  6. POJ 3181完全背包(+高精)

    思路: f[i]表示还剩i空间的方案数 套个高精 (网上有人把它拆成了两个long long) 其实这道题的高精并不难写-.. //By SiriusRen #include <cstdio&g ...

  7. JavaScript 与Document

    JavaScript JavaScript   是脚本语言, 需要有宿主文件,  他的宿主文件是HTML文件. 可以写在head中  body中 和</heml>之后   一般写在< ...

  8. codeforces 589A Email Aliases(map)

    Description Polycarp has quite recently learned about email aliases. Of course, he used to suspect t ...

  9. CCF模拟题 有趣的数

    有趣的数 时间限制: 1.0s 内存限制: 256.0MB   问题描述 我们把一个数称为有趣的,当且仅当: 1. 它的数字只包含0, 1, 2, 3,且这四个数字都出现过至少一次. 2. 所有的0都 ...

  10. 洛谷——P1043 数字游戏

    https://www.luogu.org/problem/show?pid=1043 题目描述 丁丁最近沉迷于一个数字游戏之中.这个游戏看似简单,但丁丁在研究了许多天之后却发觉原来在简单的规则下想要 ...