zoukankan      html  css  js  c++  java
  • 第23章 程序集加载和反射

    程序集加载和反射,实现了在编译时对一个类型一无所知的情况下,如何在运行时发现类型的信息,创建类型的实例以及访问类型的成员。显现的功能以及效果是十分强大的,比如使用第三方提供的程序集,以及创建动态可扩展应用程序。

    23.1 程序集加载

    JIT编译器在将方法的IL代码编译成本地代码时,会查看IL代码中引用了哪些类型。在运行时,JIT编译器查看元数据表TypeRef和AssemblyRef来确定哪一个程序集定义了所引用的类型。在AssemblyRef元数据表的记录项中,包含了构成程序集强名称的各个部分 :名称(无扩展名和路径),版本,语言文化和公钥(PublicKeyToken)。

    JIT编译器利用以下这些信息,连接成字符串。例如:( StrongNameDLL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=07f452de4cf765d5 )。然后尝试将一个匹配的程序集加载到AppDomain中。如果是弱命名程序集,则只包含程序集的名称。

    常见的程序集加载方式有三种:

    (1)Assembly.Load

    在内部,CLR使用System.Reflection.Assembly类的静态方法Load来尝试加载程序集,常用的版本原型:

    public class Assembly
    {
        public static Assembly Load(AssemblyName assemblyRef);
        public static Assembly Load(String assemblyString);
    }

    首先熟悉下这两个方法的使用,创建个强命名程序集,然后查看强命名信息(SN.exe, Reflector工具, Assembly.GetAssemblyName)

    Assembly assemblyLoadString = Assembly.Load("StrongNameDLL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=07f452de4cf765d5");

    Assembly assemblyLoadRef = Assembly.Load(new AssemblyName("StrongNameDLL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=07f452de4cf765d5"));

    注意:

    • 对于强命名程序集,在内部,Load导致CLR向程序集应用一个版本绑定重定向策略,并在GAC中查找程序集。如果没有找到,就接着去应用程序的基目录,私有路径子目录和codebase位置中查找。
    • b.如果Load时传递的是一个弱命名程序集,Load就不会向程序集应用一个版本绑定重定向策略,CLR也不会去GAC中查找程序集。
    • 如果找到指定的程序集,返回的是那个程序集的一个Assembly对象的引用;如果没有找到指定的程序集,抛出System.IO.FileNotFoundException。
    • 对于需要加载为特定CPU架构生成的程序集,在指定程序集标识时,还可包括一个进程架构部分。

    (2)Assembly.LoadFrom

    使用Load时,它要求你事先掌握构成程序集标识的各个部分。在某些情况下,我们也可以指定一个程序集文件的路径名(包括文件扩展名),获取Assembly对象。

    Assembly assemblyFromPath = Assembly.LoadFrom(@"E:StrongNameDLL.dll");

    对于使用LoadFrom,传入路径名的使用方式,需要了解内部的实现机制,避免误用的情况:

    • 在内部,LoadFrom首先会调用System.Reflection.AssemblyName类的静态方法GetAssemblyName。

    该方法打开指定的文件,提取AssemblyRef记录项中的程序集标识信息,然后以一个System.Reflection.AssemblyName对象的形式返回这些信息(文件同时关闭)。

    • LoadFrom方法在内部调用Assembly的Load方法,将AssemblyName对象传给它。
    • CLR会为应用版本绑定重定向策略,并在各个位置查找匹配的程序集。如果Load找到了匹配的程序集,就会加载它,并返回一个Assembly对象;LoadFrom返回这个值。如果没有找到匹配项,LoadFrom就会加载路径名中的程序集。

    (3)Assembly.LoadFile

    加载指定路径上的程序集文件。

    Assembly assemblyFromPath = Assembly.LoadFile(@"E:StrongNameDLL.dll");

    注意:

    • 可将具有相同标识的一个程序集多次加载到一个AppDomain中
    • CLR不会解析任何依赖性问题
    • 必须向AppDomain的AssemblyResolve事件登记,并让事件回调方法显式地加载任何依赖的程序集

    三者之间的区别与对比:
    既然已经对Load,LoadFrom,LoadFile有所了解,那么接着来看看这三者之间的区别与对比。

    1.LoadLoadFrom

    • 根据LoadFrom的内部实现机制,LoadFrom返回的实际是Load找到的匹配程序集的一个Assembly对象(在找到匹配的程序集的情况下)
    • LoadFrom存在多次打开文件的现象,并且内部还是要走一套Load的逻辑操作,还存在对比情况,所以LoadFrom比Load效率低
    • LoadFrom 要求指定路径中包含 FileIOPermissionAccess.Read 和 FileIOPermissionAccess.PathDiscovery 或 WebPermission
    • LoadFrom, and the probing path includes an assembly with the same identity but a different location, an InvalidCastException, MissingMethodException, or other unexpected behavior can occur. "> 如果用 LoadFrom 加载一个程序集,并且probing路径包括具有相同标识但位置不同的程序集,则发生 InvalidCastException 、 MissingMethodException 或其他意外行为。

    2.LoadFromLoadFile

    • 根据MSDN的解释,可以看出功能实现机制存在区别。

      LoadFrom:已知程序集的文件名或路径,加载程序集。

      LoadFile:加载指定路径上的程序集文件的内容。

    • LoadFrom会加载程序集的依赖项,但是LoadFile不会。

      例如,上面的StrongName.dll存在引用程序集ReferenceDemo.dll。使用LoadFrom,StrongName.dll和 ReferenceDemo.dll都会被载入,但是使用LoadFile,ReferenceDemo.dll不会被载入。

    • 可以使用LoadFile加载并检查具有相同标识但位于不同路径的程序集,但是LoadFrom不行, LoadFrom returns the loaded assembly even if a different path was specified. ">如果已加载一个具有相同标识的程序集,则即使指定了不同的路径,仍返回已加载的程序集。 。

      例如,StrongName.dll存在于两个不同路径下,LoadFile可以正确载入两个程序集;但是LoadFrom如果已经加载了一次 StrongName.dll,再一次加载不同路径下的Assembly时,会先检查前面是否已经载入过相同名字的 Assembly,最终返回的还是第一次加载的Assembly对象的引用。

    总结:

    由于LoadFrom具有不少缺点,一般优先考虑使用Load方法加载程序集。而LoadFile则只在有限的情况下使用。

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

    概述反射

    • 通过反射可以提供类型信息,从而使得我们开发人员在运行时能够利用这些信息构造和使用对象。简单通俗的说,就是动态调用编译过的程序集中的属性,字段,方法等的过程,就是反射。 
    • 反射机制允许程序在执行过程中动态地添加各种功能。  

    运行时类型标识

    • 运行时类型标识(RTTI),可以在程序执行期间判定对象类型。例如使用它能够确切地知道基类引用指向了什么类型对象。
    • 运行时类型标识,能预先测试某个强制类型转换操作,能否成功,从而避免无效的强制类型转换异常。 
    • 在c#中有三个支持RTTI的关键字:is 、 as  、typeof。 下面依次介绍他们 

    is运算符:

    通过is运算符,能够判断对象类型是否为特顶类型,如果两种类型是相同类型,或者两者之间存在引用,装箱拆箱转换,则表明两种类型是兼容的。

    class Program
        {
            static void Main(string[] args)
            {
                A a = new A();
                B b = new B();
                if (a is A)  
                {
                    Console.WriteLine("a is an A");   //这个打印,因为a 是 A 类型的对象
                }
                if (b is A)
                {
                    //这个打印,因为b是B类型的对象,而B类型派生于A类型,由于b对象可以转换为A类型,因此b对象与A类型是兼容的,但是反过来就不成立,例如下面不打印
                    Console.WriteLine("b is an A because it is derived from"); 
                }
                if (a is B)
                {
                    //这个不打印
                    Console.WriteLine("This won't display , because a not derived from B");
                }
                if (a is object)
                {
                    //这个打印
                    Console.WriteLine("a is an object");
                }
                Console.ReadKey();
            }
        }
        class A { }
        class B : A { }

    as运算符:

    在运行期间执行类型转换,并且能够使得类型转换失败不抛异常,而返回一个null值,其实as也可以看作一个is运算符的简化备选方式(看例子)。

    class Program
        {
            static void Main(string[] args)
            {
                A a = new A();
                B b = new B();
                if (a is B)
                {
                    b = (B)a;   //由于a变量不是B类型,因此这里将a变量转换为B类型是无效的。
                }
                else
                {
                    b = null;
                }
    
                if (b ==null)
                {
                    //这个打印
                    Console.WriteLine("The cast in b=(B)a is not allowed"); 
                }
    
                //上面使用as运算符,能够把两部合二为一。
    
                b = a as B;   //as类型先检查强制类型转换的有效性,如果有效,则执行强类型转换过程。这些都在这一句完成。
                if (b == null)
                {
                    //这个打印
                    Console.WriteLine("The cast in b=(B)a is not allowed");
                }
                Console.ReadKey();
            }
        }
        class A { }
        class B : A { }

    typeof运算符: 

    as ,is 能够测试两种类型的兼容性。但大多数情况下,还需要获得某个类型的具体信息。这就用到了typeof,它可以返回与具体类型相关的System.Type 对象,通过System.Type对象可以去顶此类型的特征。一旦获得给定类型的Type对象,就可以通过使用该对象定义的各种属性,字段,方法来获取类 型的具体信息。Type类包含了很多成员,在接下来的反射中再详细讨论。下面简单的演示Type对象,调用它的三个属性。  

    static void Main(string[] args)
            {
                Type t=typeof(StringBuilder);
    
                Console.WriteLine(t.FullName);  //FullName属性返回类型的全称
    
                if (t.IsClass)
                {
                    Console.WriteLine("is a class"); //打印
                }
                if (t.IsSealed)  //是否为密封类
                {
                    Console.WriteLine("is Sealed");  //打印
                }
                Console.ReadKey();
            }

    反射代码实现的核心类:System.Type类

    • 许多支持反射的类都位于System.Reflection命名空间中,他们是.net  Reflection API的一部分,所以在使用的反射的程序中一般都要使用 System.Reflection的命名空间。
    • System. Type类包装了类型,因此是整个反射子系统的核心,这个类中包含了很多属性和方法,使用这些属性和方法可以在运行时得到类型的信息。
    • Type类派生于System.Reflection.MemberInfo抽象类

    MemberInfo类中的只读属性

    属性

    描述

    Type DeclaringType

    获取声明该成员的类或接口的类型

    MemberTypes  MemberType

    获取成员的类型,这个值用于指示该成员是字段、方法、属性、事件、或构造函数

    Int MetadataToken

    获取与特定元数据相关的值

    Module Module

    获取一个代表反射类型所在模块(可执行文件)的Module对象

    String  Name

    成员的名称

    Type  ReflectedType

    反射的对象类型


    请注意 

    • MemberType属性的返回类型为MemberTypes,这是一个枚举,它定义了用于表示不同成员的类型值。这些值包括:MemberTypes.Constructor,   MemberTypes.Method,      MemberTypes.Field,        MemberTypes.Event,      MemberTypes.Property。因此可以通过检查MemberType属性来确定成员的类型,例如,在MemberType属性的值为MemberTypes.Method时,该成员为方法
    • MemberInfo类还包含两个与特性相关的抽象方法:  
      1. GetCustomAttributes() :获得与主调对象关联的自定义特性列表。
      2. IsDefined(): 确定是否为主调对象定义了相应的特性。
      3. GetCustomAttributesData():返回有关自定义特性的信息(特性稍后便会提到)

    当然除了MemberInfo类定义的方法和属性外,Type类自己也添加了许多属性和方法:如下表(只列出一些常用的,太多了,自己可以转定义Type类看下) 

    Type类定义的方法

    方法

    功能

    ConstructorInfo[]  GetConstructors()

    获取指定类型的构造函数列表

    EventInfo[]   GetEvents();

    获取指定类型的时间列

    FieldInfo[]   GetFields();

    获取指定类型的字段列

    Type[]   GetGenericArguments();

    获取与已构造的泛型类型绑定的类型参数列表,如果指定类型的泛型类型定义,则获得类型形参。对于正早构造的类型,该列表就可能同时包含类型实参和类型形参

    MemberInfo[]   GetMembers();

    获取指定类型的成员列表

    MethodInfo[]   GetMethods();

    获取指定类型的方法列表

    PropertyInfo[]   GetProperties();

    获取指定类型的属性列表

    下面列出Type类定义的常用的只读属性

    Type类定义的属性

    属性

    功能

     Assembly   Assembly 

    获取指定类型的程序集

    TypeAttributes   Attributes

    获取制定类型的特性

    Type   BaseType

    获取指定类型的直接基类型

    String  FullName

    获取指定类型的全名

    bool   IsAbstract

    如果指定类型是抽象类型,返回true

    bool   IsClass

    如果指定类型是类,返回true

    string   Namespace

    获取指定类型的命名空间

    使用反射

    上面的列术都是为了,这里的使用。

    通过使用Type类定义的方法和属性,我们能够在运行时获得类型的各种具体信息。这是一个非常强大的功能。我们一旦得到类型信息,就可以调用其构造函数,方法,和属性。可见,反射是允许使用编译时不可用的代码的。

    由于Reflection API非常多,这里不可能完整的介绍他们(这里如果完整的介绍,据说要一本书,厚书)。但是,Reflection API是按照一定逻辑设计的。因此,只要知道部分接口的使用方法,就可以举一反三的使用剩余的接口。

    这里我列出四种关键的反射技术:

    • 获取方法的信息
    • 调用方法
    • 构造对象
    • 从程序集中加载类型

    获取方法的相关信息

     一旦有了Type对象就可以使用GetMethodInfo()方法获取此类型支持的方法列表。该方法返回一个MethodInfo 对象数组,MethodInfo对象描述了主调类型所支持的方法,他位于System.Reflection命名空间中

    MethodInfo类派生于MethodBase抽象类,而MethodBase类继承了MemberInfo类。因此我们能够使用这三个类定义的属性和方法。例如,使用Name属性得到方法名称。这里有两个重要的成员:

      1.  ReturnType属性  :为Type类型的对象,能够提供方法的返回类型信息
      2.  GetParameters()方法  :返回参数列表,参数信息以数组形式保存在PatameterInfo对象中。PatameterInfo类定义了大量描述参数信息的属性和方法。这里 也列出两个常用的属性 :Name(包含参数名称信息的字符串),ParameterType(参数类型的信息)。

    下面代码,我将使用反射获得类中所支持的方法,还有方法的信息。

    class MyClass
        {
            int x;
            int y;
            public MyClass(int i, int j)
            {
                x = i;
                y = j;
            }
            public int sum()
            {
                return x + y;
            }
            public bool IsBetween(int i)
            {
                if (x < i && i < y) return true;
                else return false;                
            }
            public void Set(int a, int b)
            {
                x = a;
                y = b;
            }
            public void Set(double a, double b)
            {
                x = (int)a;
                y = (int)b;
            }
            public void Show()
            {
                Console.WriteLine("x:{0},y:{1}",x,y);
            }
        }
        class ReflectDemo
        {
            static void Main(string[] args)
            {
                Type t=typeof(MyClass);   //获取描述MyClass类型的Type对象
                Console.WriteLine("Analyzing methods in "+t.Name);  //t.Name="MyClass"
    
                MethodInfo[] mi = t.GetMethods();  //MethodInfo对象在System.Reflection命名空间下。
                foreach (MethodInfo m in mi) //遍历mi对象数组
                {
                    Console.Write(m.ReturnType.Name); //返回方法的返回类型
                    Console.Write(" " + m.Name + "("); //返回方法的名称
    
                    ParameterInfo[] pi = m.GetParameters();  //获取方法参数列表并保存在ParameterInfo对象数组中
                    for (int i = 0; i < pi.Length; i++)
                    {
                        Console.Write(pi[i].ParameterType.Name); //方法的参数类型名称
                        Console.Write(" "+pi[i].Name);  // 方法的参数名
                        if (i + 1 < pi.Length)
                        {
                            Console.Write(", ");
                        }
                    }
                    Console.Write(")");
                    Console.WriteLine(); //换行
                }
    
                Console.ReadKey();
            }
        }

    输出结果为:Analyzing methods inMyClass MyClass(int i, int j)  int sum()  bool IsBetween(int i)  void Set(int a, int b)  void Set(double a, double b)  void Show()

    bool Equals(object obj)   int GetHashCode()  Type GetType()  string ToString()

    注意:这里输出的除了MyClass类定义的所有方法外,也会显示object类定义的共有非静态方法。这是因为c#中的所有类型都继承于object类。另外,这些信息实在程序运行时动态获得的,并不需要预先知道MyClass类的定义

    GetMethods()方法的另一种形式

    这种形式可以制定各种标记,已筛选想要获取的方法。他的通用形式为:MethodInfo[] GetMethods(BindingFlags bindingAttr)

    BindingFlags是一个枚举,枚举值有(很多只列出5个吧):

    1. DeclareOnly:仅获取指定类定义的方法,而不获取所继承的方法;
    2. Instance:获取实例方法
    3. NonPublic: 获取非公有方法
    4. Public: 获取共有方法
    5. Static:获取静态方

    GetMethods(BindingFlags bindingAttr)这个方法,参数可以使用or把两个或更多标记连接在一起,实际上至少要有Instance(或Static)与Public(或NonPublic)标记。否则将不会获取任何方法。

    class MyClass
        {
            int x;
            int y;
            public MyClass(int i, int j)
            {
                x = i;
                y = j;
            }
            private int sum()
            {
                return x + y;
            }
            public bool IsBetween(int i)
            {
                if (x < i && i < y) return true;
                else return false;                
            }
            public void Set(int a, int b)
            {
                x = a;
                y = b;
            }
            public void Set(double a, double b)
            {
                x = (int)a;
                y = (int)b;
            }
            public void Show()
            {
                Console.WriteLine("x:{0},y:{1}",x,y);
            }
        }
        class ReflectDemo
        {
            static void Main(string[] args)
            {
                Type t=typeof(MyClass);   //获取描述MyClass类型的Type对象
                Console.WriteLine("Analyzing methods in "+t.Name);  //t.Name="MyClass"
    
                MethodInfo[] mi = t.GetMethods(BindingFlags.DeclaredOnly|BindingFlags.Instance|BindingFlags.Public);  //不获取继承方法,为实例方法,为公开的
                foreach (MethodInfo m in mi) //遍历mi对象数组
                {
                    Console.Write(m.ReturnType.Name); //返回方法的返回类型
                    Console.Write(" " + m.Name + "("); //返回方法的名称
    
                    ParameterInfo[] pi = m.GetParameters();  //获取方法参数列表并保存在ParameterInfo对象数组中
                    for (int i = 0; i < pi.Length; i++)
                    {
                        Console.Write(pi[i].ParameterType.Name); //方法的参数类型名称
                        Console.Write(" "+pi[i].Name);  // 方法的参数名
                        if (i + 1 < pi.Length)
                        {
                            Console.Write(", ");
                        }
                    }
                    Console.Write(")");
                    Console.WriteLine(); //换行
                }
    
                Console.ReadKey();
            }
        }

    上面例子,可以看出只显示了Myclass类显示定义的公用方法。private int sum()也不显示。

    使用反射调用方法

    上面我们通过反射获取到了类中的所有信息,下面我们就再使用反射调用通过反射获取到的方法。

    要调用反射获取到的方法,则需要在MethodInfo实例上调用Invoke() 方法。Invoke()的使用,在下面例子中演示,说明。

    下面例子是:先通过反射获取到要调用的方法,然后使用Invoke()方法,调用获取到的指定方法;

    class MyClass
        {
            int x;
            int y;
            public MyClass(int i, int j)
            {
                x = i;
                y = j;
            }
            private int sum()
            {
                return x + y;
            }
            public bool IsBetween(int i)
            {
                if (x < i && i < y) return true;
                else return false;
            }
            public void Set(int a, int b)
            {
                Console.Write("Inside set(int,int).");
                x = a;
                y = b;
                Show();
            }
            public void Set(double a, double b)
            {
                Console.Write("Inside set(double,double).");
                x = (int)a;
                y = (int)b;
                Show();
            }
            public void Show()
            {
                Console.WriteLine("x:{0},y:{1}", x, y);
            }
        }
    
        class InvokeMethDemo
        {
            static void Main()
            { 
               Type t=typeof(MyClass);
               MyClass reflectOb = new MyClass(10, 20);
               reflectOb.Show();  //输出为: x:10, y:20
               MethodInfo[] mi = t.GetMethods();           
               foreach (MethodInfo m in mi)
               {
                   ParameterInfo[] pi = m.GetParameters();
    
                   if (m.Name.Equals("Set", StringComparison.Ordinal) && pi[0].ParameterType == typeof(int))
                   {
                       object[] args = new object[2];
                       args[0] = 9;
                       args[1] = 10;
                       //参数reflectOb,为一个对象引用,将调用他所指向的对象上的方法,如果为静态方法这个参数必须设置为null
                       //参数args,为调用方法的参数数组,如果不需要参数为null
                       m.Invoke(reflectOb, args);   //调用MyClass类中的参数类型为int的Set方法,输出为Inside set(int,int).x:9, y:10
                   }
               }
               Console.ReadKey();
            }
        }

    获取Type对象的构造函数

    在这之前的阐述中,由于MyClass类型的对象是都是显式创建的,因此使用反射技术调用MyClass类中的方法是没有任何优势的,还不如以普通方式 调用方便简单呢。但是,如果对象是在运行时动态创建的,反射功能的优势就会显示出来。在这种情况下,要先获取一个构造函数列表,然后调用列表中的某个构造 函数,创建一个该类型的实例。通过这种机制,可以在运行时实例化任意类型的对象,而不必在声明语句中指定类型。

    class MyClass
        {
            int x;
            int y;
            public MyClass(int i)
            {        
                x = y + i;
            }
            public MyClass(int i, int j)
            {            
                x = i;
                y = j;
            }
            public int sum()
            {
                return x + y;
            }
        }
    
        class InvokeConsDemo
        {
            static void Main()
            {
                Type t = typeof(MyClass);
                int val;
                ConstructorInfo[] ci = t.GetConstructors();  //使用这个方法获取构造函数列表
    
                int x;
                for (x = 0; x < ci.Length; x++)
                {
                    ParameterInfo[] pi = ci[x].GetParameters(); //获取当前构造函数的参数列表
                    if (pi.Length == 2) break;    //如果当前构造函数有2个参数,则跳出循环
                }
    
                if (x == ci.Length)
                {
                    return;
                }
    
                object[] consargs = new object[2];
                consargs[0] = 10;
                consargs[1] = 20;
                object reflectOb = ci[x].Invoke(consargs);  //实例化一个这个构造函数有两个参数的类型对象,如果参数为空,则为null
    
    
                //实例化后,调用MyClass中的方法
                MethodInfo[] mi = t.GetMethods(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance);
                foreach (MethodInfo m in mi)
                {
                    if (m.Name.Equals("sum", StringComparison.Ordinal))
                    {
                        val = (int)m.Invoke(reflectOb, null);  //由于实例化类型对象的时候是用的两个参数的构造函数,所以这里返回的结构为30
                        Console.WriteLine(" sum is " + val);  //输出 sum is 30
                    }
                }
    
    
                Console.ReadKey();
            }
        }

    从程序集获得类型

    在这之前的阐述中可以看出一个类型的所有信息都能够通过反射得到,但是MyClass类型本身,我们却没有做出获取。虽然前面的阐述实例,可以动态确定 MyClass类的信息,但是他们都是基于以下事实:预先知道类型名,并且在typeof语句中使用它获得Type对象。尽管这种方式可能在很多种情况下 都很管用,但是要发挥反射的全部功能,我们还需要分析程序集的内容来动态确定程序的可用类型。

    借助Reflection API,可以加载程序集,获取它的相关信息并创建其公共可用类型的实例。通过这种机制,程序能够搜索其环境,利用潜在功能,而无需在编译期间显式的定义他们。这是一个非常有效,且令人兴奋的概念。

    为了说明如何获取程序集中的类型,我创建两个文件。第一个文件定义一组类,第二个文件则反射各个类的信息。 代码效果如下。

           1.这下面代码是要编译生成MyClass.exe文件的

    class MyClass
        {
            int x;
            int y;
            public MyClass(int i)
            {
                x = y + i;
            }
            public MyClass(int i, int j)
            {
                x = i;
                y = j;
            }
            public int sum()
            {
                return x + y;
            }
        }
    
        class Demo
        {
            static void Main()
            {
                Console.WriteLine("hello word !");
                Console.ReadKey();
            }
        }

           2.这下面的代码是获取上面生成程序集的

    class Class3
        {
            static void Main() {
    
                Assembly asm = Assembly.LoadFrom(@"C:UserslenovoDocumentsvisual studio 2010ProjectsReflection_testConsoleApplication1inDebugMyClass.exe");  //加载指定的程序集
                Type[] alltype = asm.GetTypes();  //获取程序集中的所有类型列表
                foreach (Type temp in alltype)
                {
                    Console.WriteLine(temp.Name);  //打印出MyClass程序集中的所有类型名称 MyClass , Demo
                }
                Console.ReadKey();       
    
            }
        }

    上面获取到了,程序集中的类型,如果想操作程序集中类型中的方法,则跟前边我们累述的方法是一个样子的。

    下面给出一个反射简单实例:

    下面通过一个实例来学习一下反射最基本的使用方法。

    建立一个解决方案,包含两个项目,项目ClassLibrary生成一个DLL(包含两个类),另一个项目是ReflectionTestGet,用于反射调用类ClassLibrary

    第一个项目的两个类如下:

    MartialArtsMaster.cs

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ClassLibrary
    {
        /// <summary>
        /// 类:武林高手
        /// MartialArtsMaster
        /// </summary>
        class MartialArtsMaster
        {
            /// <summary>
            /// 级别
            /// </summary>
            public int _level = 9;
            /// <summary>
            /// 编号
            /// </summary>
            public int Id { get; set; }
            /// <summary>
            /// 姓名
            /// </summary>
            public string Name { get; set; }
            /// <summary>
            /// 年龄
            /// </summary>
            public int Age { get; set; }
            /// <summary>
            /// 门派
            /// </summary>
            public string Menpai { get; set; }
            /// <summary>
            /// 武学
            /// </summary>
            public string Kungfu { get; set; }
            /// <summary>
            /// 级别
            /// </summary>
            public int Level
            {
                get
                {
                    return _level;
                }
    
                set
                {
                    _level = value;
                }
            }
    
            /// <summary>
            /// 攻击
            /// </summary>
            /// <param name="kungfu"></param>
            /// <returns></returns>
            public string Attack(string kungfu)
            {
                return "使用用了功夫:" + kungfu;
            }
    
            public string Kill(string kungfu, string name)
            {
                return "使用用了功夫:" + kungfu + "击杀了" + name;
            }
        }
    }
    复制代码

    Person.cs

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ClassLibrary
    {
        /// <summary>
        /// 类:人
        /// </summary>
        class Person
        {
            public string gender { get; set; }
            public string race { get; set; }
            public string Country { get; set; }
            public string Eat(string strCountry)
            {
                switch (strCountry)
                {
                    case "美国":
    
                        return "爱吃西餐";
    
                    case "韩国":
    
                        return "爱吃泡菜";
                    default:
                        return "不知道";
                }
            }
        }
    }
    复制代码

    第二个项目调用如下:

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Reflection;
    
    
    namespace ReflectionTestGet
    {
        class Program
        {
            static void Main(string[] args)
            {
    
                Assembly asm = Assembly.LoadFrom("ClassLibrary.dll");  //加载指定的程序集
                Type[] alltype = asm.GetTypes();  //获取程序集中的所有类型列表
                foreach (Type calssName in alltype)
                {
                    Console.WriteLine("加载程序的集类名:"+ calssName.Name);  //打印出程序集所有类
                    foreach (var field in calssName.GetFields())
                        Console.WriteLine(calssName.Name+"字段有:" + field.Name);  //打印出程序集所有字段,注意只能获取公有字段
                    foreach (var pro in calssName.GetProperties())
                        Console.WriteLine(calssName.Name + "属性有:" + pro.Name);  //打印出程序集所有属性
                    foreach (var met in calssName.GetMethods())
                        Console.WriteLine(calssName.Name + "方法有:" + met.Name);  //打印出程序集所有方法
    
                }
                Console.ReadKey();
            }
        }
    }
    复制代码

    运行结果如下:

  • 相关阅读:
    Java Collection知识总结
    Java异常总结
    关于触发器
    关于事务
    git分支的创建、删除、切换、合并
    github项目上传管理
    如何在github上下载单个文件夹?
    常见的javascript跨站
    各类常用端口漏洞缺陷
    SEO优化实践操作
  • 原文地址:https://www.cnblogs.com/chrisghb8812/p/5618189.html
Copyright © 2011-2022 走看看