zoukankan      html  css  js  c++  java
  • 【框架设计】程序集的加载与反射

         程序集的加载与反射

    概述:宿主为什么在运行时发现插件的原因?信息通常用于创建动态的可扩展性的应用程序。这种类型应用程序可以由一家公司构建宿主应用程序,其他公司可以创建插件以扩展宿主应用程序。宿主应用程序不可能在插件上构建和测试,因为插件有不同公司构建的,而且还极有可能在宿主应用程序发布后创建。

    C#基础系列导航


    1. C#实现队列读写操作(一)
    2. 变化多端的列表(二)
    3. VS自动内存管理(垃圾回收集)(三)
    4. C#忽略基础知识点梳理(四)
    5. 什么是框架的接口(五)
    6. 程序集的加载与反射(六)
    7. CLR寄宿和应用程序域(七)
    8. 异常(八)

    【引子】宿主为什么在运行时发现插件的原因?

    信息通常用于创建动态的可扩展性的应用程序。这种类型应用程序可以由一家公司构建宿主应用程序,其他公司可以创建插件以扩展宿主应用程序。宿主应用程序不可能在插件上构建和测试,因为插件有不同公司构建的,而且还极有可能在宿主应用程序发布后创建。

    程序集加载:

    1  //应用程序域加载程序集
    2         public static Assembly Load(AssemblyName assemblyref);//首先方法
    3         public static Assembly Load(String assemblyString);
    4         public static Assembly ReflectionOnlyLoadFrom(String assemblyFile);//加载指定路径
    5         public static Assembly LoadFrom(String path);//加载指定路径的程序集
    6         //加载指定路径的程序集
    7          private static void SomeMethod(){
    8          Assembly a=Assembly.LoadFrom(@"http://www.baidu.com/xxx.dll");
    9        }

    注,System.AppDomain也提供一个Load方法,和Assembly的静态方法Load不同.应用程序域加载一个实例方法,将程序域加载一个指定的应用程序域中.

    使用反射构建构建动态可扩展应用程序:

    反射的元数据表:元数据存储在一大堆表中,在生成一个程序集或模块时,我们所有的编译器创建一个类型定义表,一个字段定义表,一个方法定义表等.反射一般应用在类库中.

    反射的性能和例子:

    首先提到反射功能非常强大,诸如序列化格式器,反汇编器,程序集器,vs设计器等都是使用反射技术.但是这也有两个缺点

    1,反射在编译时不提供类型的安全

    2,反射的速度慢:反射要不断的扫描程序集元数据,执行Reflection命名空间中的类型字符串搜索.

    3影响性能:使用反射方法时,必须将参数打包成一个数组,而反射内部还必须将参数的数组解包到线程堆栈.

    发现程序集中定义的类型:

    反射经常判断一个程序集中定义了那些类型?

    最常用方法:Assembly和GetexportedType方法

    例子:

    1        private static void LoadAssemAndShowPulicType(string assemId){
    2            Assembly a=Assembly.Load(assemId);//显示将程序集加载到应用程序域
    3            //对每个已加载的程序集中导出类型执行以下循环
    4           foreach(Type t in a.GetExportedTypes()){
    5               Console.WriteLine(t.FullName);//显示类型的全名
    6           }
    7        }

    入口方法:

    1         public  static void Main(string[] args)
    2         {
    3                 string dataAssembly="系统日期。版本=2.0.0.0,"+"culture=neutral,PublicKeyToken=b77a55555";
    4                 LoadAssemAndShowPulicType(dataAssembly);
    5         }

    设计支持插件的应用程序:
    设计这类应用程序方法如下:

    1,创建一个定义了一个接口的"宿主SDK"程序集,该接口的方法作宿主应用程序和插件之间的沟通机制.

    2,插件开发人员将自己的"插件"程序集中定义自己的类型.

    3,创建一个单独的包含应用程序的"宿主应用程序"程序集.:该程序集引用"宿主SDK"程序集,并使用其定义的类型

    4,应用程序集都是相互隔离的,但是要想修改一个引用程序集定义的类型,必须修改版本号来实现

    以下是构建插件程序集的步骤和代码:

    1,编写HostSDK程序集,构造接口IAddId:在控制台应用程序中,通过创建类库,HOstSDK.cs,生成后在bin文件夹有经编译后生成的HOstSDK.dll

    1 namespace HostSDK
    2 {
    3     public interface IAddId
    4     {
    5         String DoSomething(string  x);
    6     }
    7 }

    2编写AddInTypes.dll程序集,实现公共接口类型,需要引用HOstSDK.dll程序集

     1 namespace AddInTypes
     2 {
     3     public sealed  class AddIn_A:IAddId
     4     {
     5         public AddIn_A() { 
     6         }
     7         public String DoSomething(string x)
     8         {
     9             return "AddIn_A:" + x;
    10         }
    11     }
    12     public sealed class AddIn_B : IAddId
    13     {
    14         public AddIn_B()
    15         {
    16         }
    17         public String DoSomething(string x)
    18         {
    19             return "AddIn_B:" + x+"您好!";
    20         }
    21     }
    22 }

    3,最后编译一个Host.exe应用程序,需要引用HostSDK.dll程序集.为了发现插件类型,假定以.dll扩展名结尾的程序集为我们搜索目标.并将程序集部署在Host.exe文件相同的目录下(拷贝HostSDk类库中bin文件夹生成的HostSDK.dl到Host的bin目录下l)

     1 namespace ConsoleApplication1
     2 {
     3    static  class Program
     4     {
     5         public  static void Main(string[] args)
     6         {
     7                 ///设计支持插件的应用程序
     8                 //查找Host.exe所在目录
     9                 String AddInDir = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
    10                 //假设插件程序集与宿主执行文件在一个目录
    11                 String[] AddInAssemblies = Directory.GetFiles(AddInDir, "*.dll");
    12                 //创建一个可用的插件类型集合
    13                 List<Type> AddInTypes = new List<Type>();
    14                 //加载程序集,并查看那个类型宿主可用
    15                 foreach (String file in AddInAssemblies)
    16                 {
    17                     Assembly AddInAssembly = Assembly.LoadFrom(file);
    18 
    19                     //检查每个公开导出类型
    20                     foreach (Type t in AddInAssembly.GetExportedTypes())
    21                     {
    22                         //如果类型实现是实现插件接口,那么类型就对宿主可用
    23                         if (t.IsClass && typeof(IAddId).IsAssignableFrom(t))
    24                         {
    25                             AddInTypes.Add(t);
    26                         }
    27                     }
    28                 }
    29                 //初始化完成,宿主已经发现可用插件,下面示范宿主如何构建插件对象并使用他们
    30                 foreach (Type t in AddInTypes)
    31                 {
    32                     IAddId ai = (IAddId)Activator.CreateInstance(t);
    33                     Console.WriteLine(ai.DoSomething("王浩"));
    34                 }
    35             Console.Read();
    36 
    37 }

    4,运行结果:

    5,如果我们 Console.WriteLine(ai.DoSomething(5));那么仅仅在AddInTypes中修改Public String DoSomething(int x);是不够的.这时需要修改AddInTypes.dll中的版本号,才能达到预期效果

  • 相关阅读:
    【linux基础】linux系统日志设置相关记录
    【linux基础】mount: unknown filesystem type 'exfat'
    [c++]float assign
    第6章 移动语义和enable_if:6.1 完美转发
    第5章 技巧性基础:5.7 模板模板参数
    第5章 技巧性基础:5.6 变量模板
    第5章 技巧性基础:5.4 原生数组和字符串字面量的模板
    第5章 技巧性基础:5.3 this->的使用
    第5章 技巧性基础:5.2 零初始化
    第5章 技巧性基础:5.1 关键字typename
  • 原文地址:https://www.cnblogs.com/baiboy/p/c6.html
Copyright © 2011-2022 走看看