反射使用基础

1. 反射技术基础   

2. Type类   

3. 动态对像创建与方法调用   

1.反射技术基础

反射(Reflection)是.NET中的一个重要技术,通过反射可以在运行时获得某个类型的各种信息,包括方法、属性、事件、以及构造函数等。还可以获得每个成员的名称、访问权限和参数等信息,由于这些信息都保存在程序集的元数据中,因此反射处理的对像是程序集元数据。利用反射技术,即可对每一个类型了如指掌。知道了类型的相关信息,就可以在程序运行时动态地创建对像,调用方法、设置属性和激发事件,所有这些都不是编译时完成的。.NET Framework甚至提供了一种方法,运行时在内存中直接无中生有的创建一个程序集(Assembly)向程序集中添加类型(type),通过ILGenerator对像直接向类型方法注入IL指令并直接执行之。

.NET程序集的层次结构
C#程序由一个或多个源文件组成。程序中声明类型(type),类型包含”成员”(Member)比如典型的类中就有方法,属性,字段和事件等成员,并且可按命名空间(Namespace)进行组织,类和接口就是类型的示例。在编译C#程序时,它们被打包为程序集(Assembly),
程序集通常具有文件扩展名.exe或.dll,前者是独立的可执行程序,后者必需被加载到一个进程之后,其包含的代码才能被执行。
每个程序集都由一个或若干个模块(Moudle)组成,模块包含IL指令与资源,但不包含程序集清单,以EXE或DLL文件的形式存在。
绝大数情况下,一个程序集只包含一个模块,换句话说模块文件(DLL或EXE)就是程序集本身。

NET Framework以一系列的类型来表达上述概念

类型名 说明
Assembly  程序集
Module 模块
ConstructorInfo  构造函数
MethodInfo  方法
FieldInfo  字段
PropertyInfo  属性
EventInfo  事件
ParameterInfo  参数

每个特定的程序集都由一个Assembly类的实例表示。Assembly类提供了一些静态方法用于得到这个对像的引用

获取特定类型所在程序集引用:Console.WriteLine(Assembly.GetAssembly(typeof(Program)).Location);   //program是定义的一个类 也可以用:Console.WriteLine(typeof(Program)Assembly.Location);

获取包含了当前正在执行的代码的程序集引用:Console.WriteLine(Assembly.GetExecutingAssembly().Location);

获取入口程序集引用:入口程序集,指的是进程的默认应用程序域中首先启动的那个程序集,比如控制台应用程序中Main方法所在的那个程序集,如果是其它的应用程序域,通常是指这一应用程序域调用ExecuteAssembly方法所执行的第一个程序集。 如以下代码输出当前应用程序域中入口程序集的完整地址 Console.WriteLine(Assembly.GetEntryAssembly().Location);

2.Type类

Type类是一种特殊的数据类型,所有数据类型都有属于自已的特定信息。
首先来看一段代码
    public class MyClass
    {
        public int MyField;
        public void MyMethod() { }
        public string MyProperty { get; set; }
    }
    class Program
    {
        static void Main(string[] args)
        {
            MyClass obj = new MyClass(); //创建对象
            //获取类型对象
            Type typ = obj.GetType();

            //输出类的名字
            Console.WriteLine(typ.Name);
            //判断其是否公有的
            Console.WriteLine(typ.IsPublic);
            //判断两个对象是否属于同一类型
            Console.WriteLine(IsTheSameType(obj,new MyClass()));
            Console.ReadKey();

        }
        static bool IsTheSameType(Object obj1, Object obj2)
        {
            return (obj1.GetType() == obj2.GetType());
        }
    }

上述代码先创建了一个MyClass类型的对像,通过调用GetType方法(从Object类继承)得到一个Type类型的对像,根据此对像即可知道MyClass这一类型的类型名,是否公在等信息,判断两个对像是否属于同一类型,可以直接比对其Type对像是否为同一个,具体看IsTheSameType方法。

创建Type对像

通过对像创建
.Net Framework中所有的数据类型都派生自Object类,而Object类提供了GetType方法,所以可以通过对所在的对对像调用GetType演绎法儿取其数据类型信息
int i = 100;
Type type = i.GetType();
Console.WriteLine(type.Name);  //输出为int32

通过语言关键字创建
c#提供了一个typeof对像,可以根据数据类型直接获取对应的Type对像
如:Type type=typeof(int);

通过传入的参数名获取Type
GetType有几个重载形式,
常用的是以下这种:public static Type GetType(string name),需要注意的是传入的参数必须是完整的类型限定名,比如"System.Int32",而不是是仅为"Int32"
代码示例:
namespace MyNameSpace
{
    namespace ChildSpace
    {
        public class Outer
        {
            public class Inner{ }
        }
    }
}
以上代码要获取outer所对应的type对像: Type type = Type.GetType("MyNameSpace.ChildSpace.Outer");
上述代码中还有一个类Inner,所有代码都在类的内部,这种类称为内部类或嵌套类,使用+来构造内部类的类型名,
Type type3 = Type.GetType("MyNameSpace.ChildSpace.Outer+Inner");

获取了Type对像之后,即可使用它的各种方法来获取特定数据类型的各种信息,下面这段示例代码获取MyClass.dll中类UserInfo下的各个成员并显示到窗体上,结果如

       

//MyClass定义如下
namespace MyClass
{
    public class UserInfo
    {
        string strT;
        public UserInfo()
        {
            strT = "aaa";
        }
        public UserInfo(string a, string b)
        {
            strT = a + b;
        }
        public int Add(int i)
        {
            return i;
        }
        public int Subtract(int i, int j)
        {
            return i - j;
        }
        public string Name
        {
            get;
            set;
        }
        public string Age
        {
            get;
            set;
        }
        public string Tel;
        public string Mobile;

    }
}
      //获取MyClass.dll中UserInfo类的各个成员
        public void ReflecForClass()
        {
            Assembly assembly = Assembly.LoadFrom("MyClass.dll");
            object obj = assembly.CreateInstance("MyClass.UserInfo");
            Type type = obj.GetType();
            //构造函数
            ConstructorInfo[] cons = type.GetConstructors();
            string constructTemp = "" ;
            foreach (ConstructorInfo con in cons)
            {
                constructTemp += con.ToString() + " ";
            }
            txtConstructor.Text = constructTemp;
            //方法
            MethodInfo[] meths = type.GetMethods();
            string methodTemp = "";
            foreach (MethodInfo meth in meths)
            {
                methodTemp += meth.ToString() + " ";
            }
            txtMethod.Text = methodTemp;
            //属性
            PropertyInfo[] props = type.GetProperties();
            string propertyTemp = "";
            foreach (PropertyInfo prop in props)
            {
                propertyTemp += prop.ToString() + " ";
            }
            txtProperty.Text = propertyTemp;
            //字段
            FieldInfo[] fields = type.GetFields();
            string fieldTemp = "";
            foreach (FieldInfo field in fields)
            {
                fieldTemp += field.ToString() + " ";
            }
            txtField.Text = fieldTemp;
        }

Type类获取类型信息的各种方法总结为:

 3.动态对像创建与方法调用

首先看个例子,一个四则运算器,这个例子不一样的地方是基于反射开发的。

首先会定义一个接口
namespace InterfaceLib
{
    public interface IMathOpt
    {
        double GetIt(double x, double y);
    }
}
再定义一个MathLibrary库,定义了加、减、乘、除四种运算,所有类都实现了接口库中的IMathOpt接口
namespace MathOptLibrary
{
    public class AddClass:IMathOpt
    {
        double IMathOpt.GetIt(double x, double y)
        {
            return x + y;
        }

    }
    public class SubstructClass : IMathOpt
    {
        double IMathOpt.GetIt(double x, double y)
        {
            return x - y;
        }
    }
    public class MultiplyClass :IMathOpt
    {
        double IMathOpt.GetIt(double x, double y)
        {
            return x * y;
        }
    }

    public class DivideClass :IMathOpt
    {
        double IMathOpt.GetIt(double x, double y)
        {
            return x / y;
        }
    }
}
启动项目DynamicCreatedObject引用InterfaceLib,但不需要引用MathLibrary.dll       
private void btnResult_Click(object sender, EventArgs e)
{
    IMathOpt mathopt;
    Assembly assembly = Assembly.LoadFrom("MathOptLibrary.dll");
    switch (cbxOpt.SelectedItem.ToString())
    {
      case "+":
        mathopt =(IMathOpt)assembly.CreateInstance("MathOptLibrary.AddClass");
        break;
      case "-":
        mathopt = (IMathOpt)assembly.CreateInstance("MathOptLibrary.SubstructClass");
        break;
      case "*":
        mathopt = (IMathOpt)assembly.CreateInstance("MathOptLibrary.MultiplyClass");
        break;
      case "/":
         mathopt = (IMathOpt)assembly.CreateInstance("MathOptLibrary.DivideClass");
         break;
     }
    labResult.Text = mathopt.GetIt(Convert.ToInt32(txtNumA.Text), Convert.ToInt32

(txtNumB.Text)).ToString();
}
当用户从下拉框中选择一种运算时,使用Assembly类的createInstance方法创建对像,然后再使用它来完成计算,此项目关键点是DynamicCreatorObject项目中没有对MathLibrary项目的引用 ,只有对接口库InterfaceLib的引用,主程序中针对IMathOpt接口编程,没有任何代码绑定到MathLibrary中的具体类型,这就剥离了界面代码与功能代码,使这两者可以独立的变化,比如可以提供多种不同的用户界面,或者提供使用不同的算法实现功能代码,这样整个程序的可扩展性大大增加,不会出现牵一发而动全身的现像。

原文地址:https://www.cnblogs.com/75115926/p/3172552.html