应用程序配置和动态加载5----反射程序集(转载)

时间:2022-01-02 03:13:24

在.Net中,程序集是进行部署、版本控制的基本单位,它包含了相关的模块和类型,同时也包含了描述这些类型的元数据。在System.Reflection命名空间下有一个Assembly类型,它代表了一个程序集,并包含了关于程序集的信息。

1.在程序中加载程序集

在程序中加载程序的方法主要有下面的几种方法。

1.1.隐式加载

没有被显式加载但被引用的程序集,CLR会按照全局程序集缓存(GAC)、工作目录(应用程序所在目录)以及私有路径目录的顺序来寻找并加载。

1.2. Assembly.Load方法

通过接受一个程序集标识来加载程序集。如果是强命名程序集,则标识包括程序集名称、版本、语言文化、以及公有密钥标记,Load方法将导致CLR按照隐式加载的策略寻找并加载程序集。弱命名程序集则只是一个不带文件扩展名的程序集的名称,CLR不会到GAC中查找,如果没有指定私有目录,则在工作目录查找,如Assembly.Load("Math")。其中私有目录的定义可以在配置文件中指定。在上节中已经给出了一个指定一个私有目录的配置文件的模板,现在给出一个指定多个私有目录的XML样本。如应用程序MyApp.exe的配置文件可以定义为MyApp.exe.config。内容:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <runtime>

    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">

      <probing privatePath="App;App1;" />

    </assemblyBinding>

  </runtime>

</configuration>

其中App和App1是两个在工作目录下的目录,用probing定义,存放需要在这个目录下被加载的程序集。如果使用工作目录以外的目录下的程序集,则可以使用codeBase元素来定义,具体不再演示。

1.3. Assembly.LoadFrom方法

当将一个程序集添加到项目引用中以后,可以直接写“文件名.dll”。如:

Assembly asm = Assembly.LoadFrom("Math.dll");

如果想加载一个不属于当前项目的程序集,则需要给出全路径。所以参数是包括程序集清单的文件的名称或路径,包括文件扩展名。如需要加载D:/App/math.dll,可以使用语句:

    Assembly a = Assembly.LoadFrom(@"D:/App/math.dll");

    a即是加载的程序集。

    但这种方法有缺点,在MSDN上有详细的描述,问题是加载一个在非工作目录下的程序集后,在另一个程序集中使用这个程序集中的对象进行反序列化时失败。使用Assembly.Load方法或者将该程序集放在工作目录下后问题得以解决。

1.4. Assembly.LoadFile方法

用来加载指定路径上的程序集文件的内容。使用LoadFile方法来加载和检查具有相同标识但位于不同路径中的程序集。与LoadFrom不同,LoadFile不会将文件加载到LoadFrom上下文中,也不会使用加载路径解析依赖项。LoadFile在这个受限制的方案中很有用,因为 LoadFrom不能用于加载标识相同但路径不同的程序集;它只加载第一个这样的程序集。

1.5. Assembly.LoadWithPartialName方法

使用部分名称从应用程序目录或从全局程序集缓存加载程序集。参数为程序集标识,其中包含程序集的名称(不带文件扩展名)。程序集的版本、语言文化及公用密钥标记为可选。

该方法执行时,CLR首先检查应用程序的XML配置文件来搜索qualifyAssembly元素,若存在则该元素应能告诉CLR如何将一个部分的程序集标识映射为完全限定的标识,CRL将根据通常规则来查找程序集。

若该元素不存在,CRL将使用指定名称在应用程序的工作目录和私有路径目录中搜索。若仍未找到,则到GAC中查找。

不管上面的哪种办法,都可以加载一个程序集进入一个Assembly类型的对象中。

2.如果想获得当前程序集,可以使用Assembly类型的静态方法 GetExecutingAssembly,它返回包含当前执行的代码的程序集(也就是当前程序集)。

Assembly asm = Assembly.GetExecutingAssembly();

在获得一个Type类型实例以后,我们还可以使用该实例的Assembly属性来获得其所在的程序集:

    Type t = typeof(int);

Assembly asm = t.Assembly;

一个程序集可能有多个模块(Module)组成,每个模块又可能包含很多的类型,但.Net的默认编译模式一个程序集只会包含一个模块,现在看下反射提供的获取关于程序集的信息的功能,表11-4只列出了部分常用的:

表11-4

 / 

 

FullName

程序集标记(包含文件名、版本、区域、公钥)

Location

程序集的路径

GetTypes()

获取程序集包含的全部类型,返回一个数组

GetType()

获取某个类型,通过参数来具体指定

GetModules()

获取程序集包含的模块

GetModule()

获取某个模块,通过参数来具体指定

GetCustomAttributes()

获取自定义特性信息

为了方便测试,现在建立一个AssemblySample类库项目,然后再添加一个Windows控制台应用程序,用来得到程序集AssemblySample中的信息。取名叫TestAssembly;然后在AssemblySample类库项目中编写体现尽可能多的类型,其代码如下:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace VS2008.Chapter11.AssemblySample

{

    //抽象类

    public abstract class BaseClass

    {

 

    }

    //结构体类型

    public struct DemoStruct { }

    //委托类型

    public delegate void DemoDelegate(Object sender, EventArgs e);

    //枚举类型

    public enum DemoEnum

    {

        terrible, bad, common = 4, good, wonderful = 8

    }

    //接口类型

    public interface IDemoInterface

    {

        void SayGreeting(string name);

    }

    //接口类型

    public interface IDemoInterface2 { }

    //sealed类,并从BaseClass继承,实现接口IDemoInterface, IDemoInterface2

    public sealed class DemoClass : BaseClass, IDemoInterface, IDemoInterface2

    {

        //字段

        private string name;

        public string city;

        public readonly string title;//具有只读性质

        public const string text = "Const Field";//常量字段

        //定义了一个事件

        public event DemoDelegate myEvent;

        //属性Name

        public string Name

        {

            private get { return name; }

            set { name = value; }

        }

        //构造函数

        public DemoClass()

        {

            title = "Readonly Field";

        }

        //一个嵌套类

        public class NestedClass { }

        //公开的方法SayGreeting,具有字符串类型的参数

        public void SayGreeting(string name)

        {

            Console.WriteLine("Morning :" + name);

        }

    }

 

}

编译,生成AssemblySample.dll程序集,然后在项目TestAssembly中添加一个方法AssemblyExplore,用来得到程序集中的信息,然后在main函数中这个方法进行调用。注意这个时候在项目中并没有添加AssemblySample.dll程序集的引用,为了能够在程序中加载程序集,将AssemblySample.dll程序集拷贝到TestAssembly项目的debug目录中。项目TestAssembly的完整代码:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Reflection;

 

namespace TestAssembly

{

    class Program

    {

        static void Main(string[] args)

        {

            AssemblyExplore();

            Console.ReadKey();

        }

        /// <summary>

        /// 得到加载程序集AssemblySample中的方法

        /// </summary>

        public static void AssemblyExplore()

        {

            StringBuilder sb = new StringBuilder();

            //加载程序集,注意这里并没有在程序中添加引用

            Assembly asm = Assembly.Load("AssemblySample");

            sb.Append("FullName(全名):" + asm.FullName + "/n");

            sb.Append("Location(路径):" + asm.Location + "/n");

            //得到AssemblySample中所包含的所有类型

            Type[] types = asm.GetTypes();

            foreach (Type t in types)

            {

                sb.Append("   类型:" + t + "/n");

            }

            Console.WriteLine(sb.ToString());

        }

    }

}