第十七章 反射、特性和动态编程

来源:互联网 发布:淘宝小助手怎么设置 编辑:程序博客网 时间:2024/05/16 17:42

1、反射
反射是能在运行时获取程序集中每一个类型(包括类、结构、委托、接口和枚举等)的成员和成员信息。通过反射可以实现从对象外部了解对象内部结构的功能。

在以前,代码被编译成机器码,关于代码的所有信息(比如类、方法名等)都会被丢弃,但C#编译成CIL时,它会维持关于代码的大部分信息。通过反射可以得到这些信息。

1.1 反射主要用到的类以及用途

1.1.1 System.Reflection命名空间

  • 使用Assembly定义和加载程序集,加载程序集清单列出的模块,以及从程序集中查找类并创建类的实例
  • 使用Module了解包括模块的程序集以及模块中的类等,还可以获取在模块上定义的所有全局方法或其他特定的非全局方法
  • 使用ConstructorInfo了解构造函数的名称、参数、访问修饰符和实现信息等
  • 使用MethodInfo了解方法的名称、参数、访问修饰符和实现信息等
  • 使用FieldInfo了解字段的名称、访问修饰符和实现信息等,并获取或设置字段值
  • 使用EventInfo了解事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序
  • 使用PropertyInfo了解属性的名称、数据类型、声明类型、反射类型、只读或可写状态等,获取或设置属性值
  • 使用ParameterInfo了解参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中的位置等

1.1.2 System命名空间

  • 使用Type可以访问任何给定数据类型的信息

1.2 System.Type类

Type是一个抽象基类,每个数据类型都有对应的Type的派生类,使用这个派生类的属性、方法、字段可以查找有关该类型的所有信息。

1.2.1 获取Type引用

获取Type引用的3种常用方法:

  • typeof运算符:Type t = typeof(string);
  • 对象的GetType()方法:Type t = "sss".GetType();
  • Type类静态的GetType()方法:Type t = Type.GetType("sss");

1.2.2 Type的属性

  • Name——获取数据类型的名称
  • FullName——获取数据类型的完全限定名
  • NameSpace——获取数据类型命名空间名
  • IsAbstract——指示数据类型是否是抽象对象
  • IsArray——指示数据类型是否是数组
  • IsClass——指示数据类型是否是类
  • IsEnm——指示数据类型是否是枚举
  • IsInterface——指示数据类型是否是接口
  • IsPublic——指示数据类型是否是公有的
  • IsSealed——指示数据类型是否是密封类
  • IsValueType——指示数据类型是否是值类型

1.2.3 Type的方法

  • GetConstructor(), GetConstructors()——用于获取类的构造函数信息,返回ConstructorInfo类型
  • GetEvent(), GetEvents()——用于获取类的事件信息,返回EventInfo类型
  • GetField(), GetField()——用于获取类的字段信息,返回FieldInfo类型
  • GetInterface(), GetInterfaces()——用于获取类的接口信息,返回InterfaceInfo类型
  • GetMember(), GetMembers()——用于获取类的成员信息,返回MemberInfo类型
  • GetMethod(), GetMethods()——用于获取类的方法信息,返回MethodInfo类型
  • GetProperty(), GetPropertys()——用于获取类的属性信息,返回PropertyInfo类型

1.3 System.Reflection.Assembly类

Assembly类可以获取程序集的信息,也可以动态的加载程序集,以及在程序集中查找类型信息,并创建该类型的实例

1.3.1 获取程序集

  • Assembly ass = Assembly.Load("ClassLibrary");——通过程序集名称获取Assembly
  • Assembly ass = Assembly.LoadFrom("ClassLibrary.dll");——通过程序集dll获取Assembly
  • Assembly ass = Assembly.LoadFile("D:ClassLibrary.dll");——通过程序集路径获取Assembly

1.3.2 获取程序集中的类

  • Type t = ass.GetType("ClassLibrary.class");——使用Assembly,通过类名(完全限定名)获取程序集中的类
  • Type[] t = ass.GetTypes();——使用Assembly获取程序集中所有的类

1.4 基于反射的实例

// ClassLibrary.dll

namespace ClassLibrary
{
    public class Student
    {
        public string myName { get; set; }
        public int myAge { get; set; }
        public Student() { }
        public Student(string name, int age)
        {
            myName = name;
            myAge = age;
        }
        public void Print()
        {
            Console.WriteLine("Name:" + myName);
            Console.WriteLine("Age:" + myAge);
        }
    }
}
// Application
namespace Application
{
    class Program
    {
        static void Main(string[] args)
        {
            // 根据路径和dll名称加载程序集
            Assembly ass = Assembly.LoadFile(AppDomain.CurrentDomain.BaseDirectory + "ClassLibrary.dll");
            Type t = ass.GetType("ClassLibrary.Student");   // 从程序集获取Student类的Type对象

           

            // 获取构造函数并使用构造函数创建对象
            Type[] test = new Type[2] { typeof(string), typeof(int) };  // 构造函数参数个数和类型
            ConstructorInfo ci = t.GetConstructor(test);   // 获取构造函数
            object[] param = new object[] { "msf", 29 };   // 创建构造函数参数
            object obj = ci.Invoke(param);   // 通过构造函数创建对象
            // 通过Type获取函数,并调用该函数
            MethodInfo mi = t.GetMethod("Print");   // 获取Print函数
            mi.Invoke(obj, new object[0]);    // 调用函数,参数分别为调用函数的对象和参数列表

                

            // 通过Type创建对象,对对象的属性赋值,并调用打印函数         

            object obj2 = t.Assembly.CreateInstance(t.FullName);   // 通过Type创建对象
            PropertyInfo fiName = t.GetProperty("myName");    // 获取对象属性
            fiName.SetValue(obj2, "test", null);      // 给属性赋值,参数是赋值对象、参数、索引
            PropertyInfo fiAge = t.GetProperty("myAge");
            fiAge.SetValue(obj2, 30, null);
            mi.Invoke(obj2, new object[0]);    // 调用方法
        }
    }
}