开课中国  |  [Investor Relations]   |  官方微博

广州.NET培训中心

美国上市公司 · 亿元级外企.NET培训企业

  • 关于.Net反射机制分析和使用

    发布:dn      来源:dn      时间:2015-11-23        

  • NET反射是审查元数据并动态收集关于它的类型信息的能力,应用程序结构分为应用程序域—程序集—模块—类型—成员几个层次,公共语言运行库加载器管理应用程序域。这些域在拥有相同应用程序范围的对象周围形成了确定边界。

  • 1..NET反射的概述


    .NET反射是审查元数据并动态收集关于它的类型信息的能力。
    应用程序结构分为应用程序域—程序集—模块—类型—成员几个层次,公共语言运行库加载器管理应用程序域。这些域在拥有相同应用程序范围的对象周围形成了确定边界。
    这种管理包括将每个程序集加载到相应的应用程序域以及控制每个程序集中类型层次结构的内存布局。程序集包含模块,而模块包含类型,类型又包含成员,反射则提供了封装程序集、模块和类型的对象。
    我们可以使用反射动态地创建类型的实例,将类型绑定到现有对象或从现有对象中获取类型,然后调用类型的方法或访问其字段和属性。


    反射的层次模型:


    2..NET反射的作用和应用


    (1).可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型;
    (2).应用程序需要在运行时从某个特定的程序集中载入一个特定的类型,以便实现某个任务时可以用到反射;
    (3).反射主要应用与类库,这些类库需要知道一个类型的定义,以便提供更多的功能;
    (4).在.NET中实现工厂模式会使用反射较多;
    (5).在JavaScript等语言编译器使用反射来构造符号表;
    (6).反射也可用于创建称为类型浏览器的应用程序,使用户能够选择类型,然后查看有关选定类型的信息;
    (7).System.Runtime.Serialization命名空间中的类使用反射来访问数据并确定要永久保存的字段;
    (8).System.Runtime.Remoting命名空间中的类通过序列化来间接地使用反射。


    3..NET反射的性能


    使用反射来调用类型或者触发方法,或者访问一个字段或者属性时clr 需 要做更多的工作:校验参数,检查权限等等,所以速度是非常慢的。 所以尽量不要使用反射进行编程,对于打算编写一个动态构造类型(晚绑定)的应用程序,可以采取以下的几种方式进行代替:


    (1).通过类的继承关系
    让该类型从一个编译时可知的基础类型派生出来,在运行时生成该类 型的一个实例,将对其的引用放到其基础类型的一个变量中,然后调用该基础类型的虚方法。


    (2).通过接口实现
    在运行时,构建该类型的一个实例,将对其的引用放到其接口类型的一个变量中,然后调用该接口定义的虚方法。

    (3).通过委托实现
    让该类型实现一个方法,其名称和原型都与一个在编译时就已知的委托相符。 在运行时先构造该类型的实例,然后在用该方法的对象及名称构造出该委托的实例,接着通过委托调用你想要的方法。这个方法相对与前面两个方法所作的工作要多一些,效率更低一些。
    提高反射的性能:反射的性能损失主要来源于比较类型、遍历成员、调用成员三种情形,其中比较类型耗时最小。 调用成员耗时最多,所以尽量减少采用成员动态调用等反射方式可以提高应用程序性能。除此之外,采取后期绑定、避免将反射方法放到循环内产生放大效应等办法均可提升反射性能。

    4..NET反射常用类型


    System.reflection命名空间下常用类型,允许你反射(解析)这些元数据表的代码和反射相关的命名空间(可以通过这几个命名空间访问反射信息):


    (1).System.Reflection.Assembly
    使用Assembly定义和加载程序集,加载在程序集清单中列出模块,以及从此程序集中查找类型并创建该类型的实例。可以使用Assembly.Load和Assembly.LoadFrom方法动态地加载程序集。


    (2).System.Reflection.Module
    使用Module了解包含模块的程序集以及模块中的类等,还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。

    (3).System.Reflection.ConstructorInfo
    使用ConstructorInfo了解构造函数的名称、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。使用Type的GetConstructors或GetConstructor方法来调用特定的构造函数。

    (4).System.Reflection.MethodInfo
    使用MethodInfo了解方法的名称、返回类型、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。使用Type的GetMethods或GetMethod方法来调用特定的方法。


    (5).System.Reflection.FieldInfo
    使用FieldInfo了解字段的名称、访问修饰符(如public或private)和实现详细信息(如static)等,并获取或设置字段值。

    (6).System.Reflection.EventInfo
    使用EventInfo了解事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序。


    (7).System.Reflection.PropertyInfo
    使用PropertyInfo了解属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,获取或设置属性值。


    (8).System.Reflection.ParameterInfo
    使用ParameterInfo了解参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中的位置等。
    System.Reflection.Emit命名空间的类提供了一种特殊形式的反射,可以在运行时构造类型。


    (9).System.Type
    System.Type类是一个抽象类,代表公用类型系统中的一种类型。这个类使您能够查询类型名、类型中包含的模块和名称空间、以及该类型是一个数值类型还是一个引用类型。 System.Type类使您能够查询几乎所有与类型相关的属性,包括类型访问限定符、类型是否、类型的COM属性等等。


    (10).System.Activator
    Activator类支持动态创建.NET程序集和COM对象。可以通过CreateComInstanceFrom、CreateInstance、CreateInstanceFrom、GetObject四个静态方法加载COM对象或者程序集,并能创建指定类型的实例。

    使用注意:
    (1).Load方法:极力推荐的一种方法,Load 方法带有一个程序集标志并载入它,Load 将引起CLR把策略应用到程序集上。 先后在全局程序集缓冲区,应用程序基目录和私有路径下面查找该程序集,如果找不到该程序集系统抛出异常。


    (2).LoadFrom方法:传递一个程序集文件的路径名(包括扩展名),CLR会载入您指定的这个程序集,传递的这个参数不能包含任何关于版本号的信息,区域性,和公钥信息,如果在指定路径找不到程序集抛出异常。


    (3).LoadWithPartialName:永远不要使用这个方法,因为应用程序不能确定再在载入的程序集的版本。该方法的唯一用途是帮助那些在.Net框架的测试环节使用.net 框架提供的某种行为的客户,这个方法将最终被抛弃不用。

    5..NET反射示例Demo


    (1).代码结构


    (2).TestClassLib类库的Arithmetic类

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;

    amespace TestClassLib
    {
    /// <summary>
    /// 定义示例接口Iwel
    /// </summary>
    public interface Iwel
    {
    string Print();
    }

    /// <summary>
    /// 构造测试数据类
    /// </summary>
    public class Arithmetic:Iwel
    {
    //定义私有变量
    private int numOne;
    private int numTwo;

    #region 构造函数测试
    //不带参数的构造函数
    public Arithmetic() { }

    //带参数构造函数,初始化numOne和numTwo
    public Arithmetic(int intNumOne, int intNumTwo)
    {
    this.numOne = intNumOne;
    this.numTwo = intNumTwo;
    }
    #endregion

    #region 属性测试
    //公共属性访问NumOne
    public int NumOne
    {
    get { return numOne; }
    set { numOne = value; }
    }

    //公共属性 访问NumTwo
    public int NumTwo
    {
    get { return numTwo; }
    set { numTwo = value; }
    }
    #endregion

    #region 方法测试
    /// <summary>
    /// 私有非静态不带参数的方法
    /// </summary>
    /// <returns></returns>
    private string Add()
    {
    return "Add()方法是一个私有不带参数的方法";
    }

    /// <summary>
    /// 私有静态带参数无返回值的方法
    /// </summary>
    /// <param name="intNumOne"></param>
    private static void Multiplcation(int intNumOne)
    {
    Console.WriteLine("Multiplication()方法是一个私有不带参数无返回值的静态方法");
    }

    /// <summary>
    /// 私有非静态带参数的方法
    /// </summary>
    /// <param name="intNumOne"></param>
    /// <param name="intNumTwo"></param>
    private void Subtration(int intNumOne, int intNumTwo)
    {
    string strMeesage = "{0}-{1}={2} Subtration(int intNumOne, int intNumTwo)方法是一个私有带参数的方法";

    Console.WriteLine(strMeesage, intNumOne, intNumTwo, intNumOne - intNumTwo);

    }

    /// <summary>
    /// 公有非静态不带参数的方法
    /// </summary>
    /// <returns></returns>
    public void Write()
    {
    Console.WriteLine( "Write() 是一个共有的不带参数无返回值的方法");
    }

    /// <summary>
    /// 公有静态带参数的方法
    /// </summary>
    /// <param name="intNumOne"></param>
    /// <param name="intNumTwo"></param>
    public static string Multiplcation(int intNumOne, int intNumTwo)
    {
    string strMessage = "{0}*{1}={2} Multiplcation(int intNumOne,int intNumTwo)方法是一个公有的静态带参数的方法";
    strMessage = string.Format("{0}*{1}={2}", intNumOne, intNumTwo, intNumOne * intNumTwo);
    return strMessage;
    }

    /// <summary>
    /// 公有非静态带参数的方法
    /// </summary>
    /// <param name="intNumOne"></param>
    /// <param name="intNumTwo"></param>
    /// <returns></returns>
    public string Add(int intNumOne,int intNumTwo)
    {
    string strMessage = string.Format("{0}+{1}={2}", intNumOne, intNumTwo, intNumOne + intNumTwo);
    return strMessage;
    }
    #endregion

    /// <summary>
    /// 集成接口方法
    /// </summary>
    /// <returns></returns>
    public string Print()
    {
    return " Print() 继承接口方法";
    }
    }
    }

    (3).TestClassLib类库的TestClass类

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;

    amespace TestClassLib
    {
    public class TestClass
    {
    //模拟测试
    public class Dialog : DialogInvoker { }

    public virtual string TestMethod(string pp)
    {
    return pp;
    }
    }
    }

    (4).TestClassLib类库的DialogInvoker类


    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;

    amespace TestClassLib
    {
    public class DialogInvoker
    {
    public int TestInt;
    public void RegisterWithKey(string strOne,string strTwo)
    {
    Console.WriteLine("Test组合类:"+strOne+","+strTwo);
    }
    }
    }

    (5).TestClassLib类库的TestDialog类


    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;

    amespace TestClassLib
    {
    public class TestDialog
    {
    //模拟测试
    public void OnInit()
    {
    ew TestClass.Dialog().RegisterWithKey("PP", "TT");
    }
    }
    }

    (6).ReflectTest的Program.cs代码调用测试

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using System.Threading.Tasks;
    using TestClassLib;

    amespace ReflectTest
    {
    class Program
    {
    //定义委托测试
    delegate void TestDelegate(int intNumOne,int intNumTwo);

    static void Main(string[] args)
    {
    SimpleTest();//简单测试

    TestArithmetic();//分析测试

    TestDialog();//模拟测试

    Console.ReadLine();
    }

    /// <summary>
    /// 基本简单测试
    /// </summary>
    static void SimpleTest()
    {
    //System.Reflection.Assembly ass = Assembly.Load("TestClassLib"); //加载DLL
    //System.Type t = ass.GetType("TestClassLib.TestChildrenClass");//获得类型

    string path = "TestClassLib.TestClass" + "," + "TestClassLib";//命名空间.类型名,程序集
    Type testType = Type.GetType(path);//加载类型

    object objType = System.Activator.CreateInstance(testType);//创建实例
    System.Reflection.MethodInfo methodReflection = testType.GetMethod("TestMethod");//获得方法

    object str = methodReflection.Invoke(objType, new object[] { "MM" });//调用方法
    Console.WriteLine("简单测试输出:"+str.ToString());
    }

    /// <summary>
    /// 反射分析测试
    /// </summary>
    static void TestArithmetic()
    {
    string strAssembly = "TestClassLib";//程序集DLL
    string strReflectClass = "TestClassLib.Arithmetic";//命名空间.类型
    string strMethodAdd = "Add";//调用方法Add名称
    string strMethodWrite = "Write";//调用方法Write名称
    string strMethodMul = "Multiplcation";//调用方法Multiplcation名称

    Assembly AssTestClassLib = Assembly.Load(strAssembly);//得到程序集
    Type ArithmeticClass = AssTestClassLib.GetType(strReflectClass);;//得到具体类型
    //Type ArithmeticClass = Type.GetType(strReflectClass + "," + strAssembly);//创建对象也可以使用:命名空间.类型名,程序集

    #region 获取程序集下的信息
    Console.WriteLine("\n得到"+strAssembly+"中所有类:" );
    foreach (Type type in AssTestClassLib.GetTypes())
    {
    Console.WriteLine(type.Name+"是"+strAssembly+"命名空间下的对象");
    }

    Console.WriteLine("\n得到"+strAssembly+".dll中的模块集:");
    Module[] modules = AssTestClassLib.GetModules();
    foreach (Module module in modules)
    {
    Console.WriteLine(module.Name+"是"+strAssembly+"下的模块集");
    }
    #endregion

    #region 获取指定类下的信息

    Console.WriteLine("\n具体类型是"+ArithmeticClass.Name);
    Console.WriteLine("{0}是不是Public类型{1}",ArithmeticClass,ArithmeticClass.IsPublic);
    Console.WriteLine("{0}是不是Privet类型{1}",ArithmeticClass,ArithmeticClass.IsPrimitive);

    Console.WriteLine("\n得到" + ArithmeticClass + "类下的构造函数:");
    ConstructorInfo [] constructorInfos = ArithmeticClass.GetConstructors();
    foreach (ConstructorInfo constructor in constructorInfos)
    {
    Console.WriteLine(constructor);
    }

    Console.WriteLine("\n得到"+ArithmeticClass+"类下的所有属性:");
    PropertyInfo[] propertyInfos = ArithmeticClass.GetProperties();
    foreach (PropertyInfo prop in propertyInfos)
    {
    Console.WriteLine(prop.Name+"是"+ArithmeticClass + "类下的属性");
    }

    Console.WriteLine("\n得到"+ArithmeticClass+"类下所有的接口:");
    Type[] typeInterfaces = ArithmeticClass.GetInterfaces();
    foreach (Type typeInterface in typeInterfaces)
    {
    Console.WriteLine(typeInterface.Name+"是"+ArithmeticClass+"类下的接口");
    }

    #region 获取所有方法的信息
    Console.WriteLine("\n得到"+ArithmeticClass+"类下所有方法:");
    //查找私有方法
    MethodInfo[] methodPrivates = ArithmeticClass.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic);
    foreach (MethodInfo method in methodPrivates)
    {
    Console.WriteLine("私有方法 方法名称:{0} 方法信息:{1}",method.Name,method);
    }
    //查找公有方法
    MethodInfo[] methodPublics = ArithmeticClass.GetMethods(BindingFlags.Instance | BindingFlags.Public);
    foreach (MethodInfo method in methodPublics)
    {
    Console.WriteLine("公有方法 方法名称:{0} 方法信息:{1}", method.Name, method);
    }
    //查找私有静态方法
    MethodInfo[] methodStaticPrivates = ArithmeticClass.GetMethods(BindingFlags.NonPublic | BindingFlags.Static);
    foreach (MethodInfo method in methodStaticPrivates)
    {
    Console.WriteLine("私有静态方法 方法名称:{0} 方法信息:{1}", method.Name, method);
    }
    //查找公有静态方法
    MethodInfo[] methodStaticPublics = ArithmeticClass.GetMethods(BindingFlags.Public | BindingFlags.Static);
    foreach (MethodInfo method in methodPrivates)
    {
    Console.WriteLine("公有静态方法 方法名称:{0} 方法信息:{1}", method.Name, method);
    }
    #endregion
    #endregion

    #region 创建对象实例
    Console.WriteLine("\n创建对象实例:");
    int num1 = 3, num2 = 5;//定义参数

    //创建一个不带参数的实例
    object obj = Activator.CreateInstance(ArithmeticClass, null);

    //公有非静态带参数和返回值的方法调用
    MethodInfo methodAdd = ArithmeticClass.GetMethod(strMethodAdd);
    object[] addParams = new object[] {num1,num2 };
    string strReturnAdd=methodAdd.Invoke(obj, addParams).ToString();
    Console.WriteLine("创建{0}类,调用公有非静态{1}方法,传入参数{2}和{3},返回值:{4}",ArithmeticClass, strMethodAdd, num1, num2, strReturnAdd);

    //私有非静态无参无返回值方法调用
    MethodInfo methodStaticAdd = ArithmeticClass.GetMethod(strMethodAdd, BindingFlags.Instance | BindingFlags.NonPublic);
    string strStaticAdd=methodStaticAdd.Invoke(obj,null).ToString();
    Console.WriteLine("创建{0}类,调用私有非静态{1}方法,返回值:{2}:", ArithmeticClass, strMethodAdd, strStaticAdd);

    //公有非静态无参数无返回值方法调用
    MethodInfo methodWite = ArithmeticClass.GetMethod(strMethodWrite, BindingFlags.Instance | BindingFlags.Public);
    Console.WriteLine("创建{0}类,调用公有非静态{1}方法,无参数无返回值:", ArithmeticClass, strMethodWrite, methodWite.Invoke(obj, null));

    //公有静态带参数有返回值的方法调用
    MethodInfo methodMultiplcation = ArithmeticClass.GetMethod(strMethodMul, BindingFlags.Public | BindingFlags.Static);
    object[] multParams = new object[] { num1, num2 };
    string multReturn = methodMultiplcation.Invoke(obj, multParams).ToString();
    //string multReturn = methodMultiplcation.Invoke(null, multParams).ToString();//调用静态方法也可以转入null对象
    Console.WriteLine("创建{0}类,调用公有静态{1}方法,传入参数{2}和{3},返回值:{4}", ArithmeticClass, strMethodMul, num1, num2, multReturn);

    //测试InvokeMember方法
    object objReturn= ArithmeticClass.InvokeMember(strMethodAdd, BindingFlags.InvokeMethod, null, obj,new object[]{num1,num2});
    Console.WriteLine("测试InvokeMember方法:创建{0}类,调用公有静态{1}方法,传入参数{2}和{3},返回值:{4}:" ,
    ArithmeticClass, strMethodAdd, num1, num2, objReturn.ToString());
    #endregion

    //动态创建委托
    Console.WriteLine("\n动态创建委托:");
    TestDelegate testDel = (TestDelegate)Delegate.CreateDelegate(typeof(TestDelegate), obj, "Subtration");
    testDel(9,3);

    }

    /// <summary>
    /// 模拟测试
    /// </summary>
    static void TestDialog()
    {
    string strAssembly = "TestClassLib";//程序集DLL
    string strDialogReflect = "TestClassLib.TestDialog";
    string strDialogInvokerReflect = "TestClassLib.DialogInvoker";
    string strDialogNestedTypeReflect = "TestClassLib.TestClass";//嵌套如何关系类,命名空间.类
    string strNestedType = "Dialog";//嵌套组合类
    string strDialogMethod = "OnInit";
    string strDialogInvokerMethod = "RegisterWithKey";

    Console.WriteLine("\n模拟测试:");

    Assembly assemblyTest = Assembly.Load(strAssembly);

    //间接调用
    Type typeDialog = assemblyTest.GetType(strDialogReflect);
    object objDialog = Activator.CreateInstance(typeDialog);
    MethodInfo methodDialog = typeDialog.GetMethod(strDialogMethod);
    methodDialog.Invoke(objDialog, null);

    //直接调用
    Type typeDialogInvoker = assemblyTest.GetType(strDialogInvokerReflect);
    object objDialogInvoker = Activator.CreateInstance(typeDialogInvoker);
    MethodInfo methodDialogInvoker = typeDialogInvoker.GetMethod(strDialogInvokerMethod);
    methodDialogInvoker.Invoke(objDialogInvoker, new object[] { "MM", "KK" });

    //使用嵌套组合关系调用(这个更实用)
    var typeTestClass =assemblyTest.GetType(strDialogNestedTypeReflect);
    //var typeTestClass = Assembly.GetExecutingAssembly().GetType(strDialogNestedTypeReflect);//如果在同一个程序集下可以使用这种方式
    var testClass= Activator.CreateInstance(typeTestClass);//创建本类

    var typeNestedDialog= typeTestClass.GetNestedType(strNestedType);//获取嵌套组合类
    var dialogClass = Activator.CreateInstance(typeNestedDialog);//创建嵌套组合类
    var methodInfo = dialogClass.GetType().GetMethod(strDialogInvokerMethod, BindingFlags.Instance | BindingFlags.Public);
    methodInfo.Invoke(dialogClass, new object[] {"TT","KK" });
    }
    }
    }

    6.运行效果如下:


  • 上一篇:知识要点:关于.Net 垃圾回收机制原理

    下一篇:【入门知识】新手该如何学习.NET?

2001-2016 达内时代科技集团有限公司 版权所有 京ICP证8000853号-56