C#学习笔记之反射

2022/3/20 22:29:48

本文主要是介绍C#学习笔记之反射,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

反射指程序可以访问、检测和修改它本身状态或行为的一种能力。

程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。

您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。

下面一个简单的反射示例,使用方法GetType以获取变量类型:

int i = 42;
Type type = i.GetType();
Console.WriteLine(type);

输出为:System.Int32


下面的示例使用反射获取已加载的程序集的完整名称。

Assembly info = typeof(int).Assembly;
Console.WriteLine(info);

输出为:System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e


反射在以下情况下很有用

需要访问程序元数据中的特性时。

检查和实例化程序集中的类型。

在运行时构建新类型。

执行后期绑定,访问在运行时创建的类型上的方法。


优点:

  • 1、反射提高了程序的灵活性和扩展性。
  • 2、降低耦合性,提高自适应能力。
  • 3、它允许程序创建和控制任何类的对象,无需提前硬编码目标类。

缺点:

  • 1、性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和拓展性要求很高的系统框架上,普通程序不建议使用。
  • 2、使用反射会模糊程序内部逻辑;程序员希望在源代码中看到程序的逻辑,反射却绕过了源代码的技术,因而会带来维护的问题,反射代码比相应的直接代码更复杂。

 

using System;
using System.Reflection;//System.Reflection 类的 MemberInfo用于发现与类相关的特性(attribute)。
namespace BugFixApplication
{
    // 一个自定义特性 BugFix 被赋给类及其成员
    [AttributeUsage
    #region//定义了特性能被放在那些前面        
        (AttributeTargets.Class |//规定了特性能被放在class的前面
        AttributeTargets.Constructor |//规定了特性能被放在构造函数的前面
        AttributeTargets.Field |//规定了特性能被放在域的前面
        AttributeTargets.Method |//规定了特性能被放在方法的前面
        AttributeTargets.Property,//规定了特性能被放在属性的前面
    #endregion
        AllowMultiple = true)]//这个属性标记了我们的定制特性能否被重复放置在同一个程序实体前多次。

    public class DeBugInfo : System.Attribute//继承了预定义特性后的自定义特性
    {
        private int bugNo;
        private string developer;
        private string lastReview;
        public string message;

        public DeBugInfo(int bg,string dev,string d)//构造函数,接收三个参数并赋给对应实例
        {
            this.bugNo = bg;
            this.developer = dev;
            this.lastReview = d;
        }
        #region//定义对应的调用,返回对应值value
        public int BugNo
        {
            get 
            {
                return bugNo;
            }
        }
        public string Developer
        {
            get
            {
                return developer;
            }
        }
        public string LastReview
        {
            get
            {
                return lastReview;
            }
        }
        //前面有public string message;
        public string Message//定义了可以通过Message = "",来对message进行赋值。
                             //因为不在构造函数中,所以是可选的
        {
            get
            {return message;}
            set
            {message = value;}
        }
        /*
         * 这部分可以简写如下
         * public string Message{get;set;}
        */
    }
    #endregion

    [DeBugInfo(45, "Zara Ali", "12/8/2012",
         Message = "Return type mismatch")]
    [DeBugInfo(49, "Nuha Ali", "10/10/2012",
         Message = "Unused variable")]//前面定义时的AllowMultiple=ture允许了多次使用在同一地方
    class Rectangle
    {
        protected double length;
        protected double width;//定义两个受保护的(封装)的成员变量
        public Rectangle(double l,double w)//构造函数,对两个成员变量进行初始化,公开的
        {
            length = l;
            width = w;
        }

        [DeBugInfo(55, "Zara Ali", "19/10/2012",
             Message = "Return type mismatch")]
        public double GetArea()
        {
            return length * width;
        }

        [DeBugInfo(56, "Zara Ali", "19/10/2012")]//因为message是可选项,所以可以不给出
                                                 //不给出即为null,为空白
        public void Display()
        {
            Console.WriteLine("Length: {0}", length);
            Console.WriteLine("Width:{0}", width);
            Console.WriteLine("Area:{0}", GetArea());//常规打印
        }
    }

    class ExecuteRectangle
    {
        static void Main(string[] args)//程序入口
        {
            Rectangle r = new Rectangle(4.5, 7.5);//实例化
            r.Display();//执行打印长、宽、面积

            Type type = typeof(Rectangle);//让type对应这个Rectangle类
            // 遍历 Rectangle 类的特性
            foreach (Object attributes in type.GetCustomAttributes(false))//遍历Rectangle的所有特性
            {
                DeBugInfo dbi = (DeBugInfo)attributes;//强制转换
                if(null != dbi)//dbi非空
                {
                    Console.WriteLine("Bug on: {0}", dbi.BugNo);
                    Console.WriteLine("Developer: {0}", dbi.Developer);
                    Console.WriteLine("Last REviewed: {0}", dbi.LastReview);
                    Console.WriteLine("Remarks: {0}", dbi.Message);
                }
            }
            // 遍历方法特性
            foreach (MethodInfo m in type.GetMethods())//遍历Rectangle类下的所有方法
            {
                foreach (Attribute a in m.GetCustomAttributes(true))//遍历每个方法的特性
                {
                    DeBugInfo dbi = a as DeBugInfo;//通过 object 声明对象,是用了装箱和取消装箱的概念.
                                                   //也就是说 object 可以看成是所有类型的父类。
                                                   //因此 object 声明的对象可以转换成任意类型的值。
                                                   //通过拆装箱代替强制转换
                    if (null !=dbi)//同理打印
                    {
                        Console.WriteLine("BugFixApplication no: {0},for Method: {1}", dbi.BugNo, m.Name);
                        Console.WriteLine("Developer:{0}", dbi.Developer);
                        Console.WriteLine("Last Reviewed: {0}", dbi.LastReview);
                        Console.WriteLine("Remarks: {0}", dbi.Message);
                    }
                }
            }
            Console.ReadKey();
        }
    }
}

.Net中获取运行时类型信息的方式,.Net的应用程序由几个部分:‘程序集(Assembly)’、‘模块(Module)’、‘类型(class)’组成,而反射提供一种编程的方式,让程序员可以在程序运行期获得这几个组成部分的相关信息,例如:

        Assembly类可以获得正在运行的装配件信息,也可以动态的加载装配件,以及在装配件中查找类型信息,并创建该类型的实例。
Type类可以获得对象的类型信息,此信息包含对象的所有要素:方法、构造器、属性等等,通过Type类可以得到这些要素的信息,并且调用之。
MethodInfo包含方法的信息,通过这个类可以得到方法的名称、参数、返回值等,并且可以调用之。诸如此类,还有FieldInfo、EventInfo等等,这些类都包含在System.Reflection命名空间下。


命名空间与装配件的关系

 装配件是.Net应用程序执行的最小单位,编译出来的.dll、.exe都是装配件。

装配件和命名空间的关系不是一一对应,也不互相包含,一个装配件里面可以有多个命名空间,一个命名空间也可以在多个装配件中存在。

装配件是一个类型居住的地方,那么在一个程序中要使用一个类,就必须告诉编译器这个类住在哪儿,编译器才能找到它,也就是说必须引用该装配件。
        那么如果在编写程序的时候,也许不确定这个类在哪里,仅仅只是知道它的名称,就不能使用了吗?答案是可以,这就是反射了,就是在程序运行的时候提供该类型的地址,而去找到它。


 委托是C#中实现事件的基础,有时候不可避免的要动态的创建委托,实际上委托也是一种类型:System.Delegate,所有的委托都是从这个类派生的
        System.Delegate提供了一些静态方法来动态创建一个委托,比如一个委托:

namespace  TestSpace  
{
      delegate  string  TestDelegate(string  value);
      public  class  TestClass 
      {
          public  TestClass()  { }
          public  void  GetValue(string  value) 
          {
               return  value;
          }
       }
}
TestClass  obj  =  new  TestClass();
//获取类型,实际上这里也可以直接用typeof来获取类型
Type  t  =  Type.GetType(“TestSpace.TestClass”);
//创建代理,传入类型、创建代理的对象以及方法名称
TestDelegate  method  =  (TestDelegate)Delegate.CreateDelegate(t,obj,”GetValue”);
String  returnValue  =  method(“hello”);

System.reflection命名空间包含的几个类,允许你反射(解析)这些元数据表的代码   

System.Reflection.Assembly 
System.Reflection.MemberInfo
System.Reflection.EventInfo
System.Reflection.FieldInfo
System.Reflection.MethodBase
System.Reflection.ConstructorInfo
System.Reflection.MethodInfo
System.Reflection.PropertyInfo
System.Type
以下是上面几个类的使用方法:
(1)使用Assembly定义和加载程序集,加载在程序集清单中列出模块,以及从此程序集中查找类型并创建该类型的实例。 
(2)使用Module了解包含模块的程序集以及模块中的类等,还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。 
(3)使用ConstructorInfo了解构造函数的名称、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。使用Type的GetConstructors或 GetConstructor方法来调用特定的构造函数。 
(4)使用MethodInfo了解方法的名称、返回类型、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。使用Type的GetMethods或GetMethod方法来调用特定的方法。 
(5)使用FiedInfo了解字段的名称、访问修饰符(如public或private)和实现详细信息(如static)等,并获取或设置字段值。 
(6)使用EventInfo了解事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序。 
(7)使用PropertyInfo了解属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,获取或设置属性值。 
(8)使用ParameterInfo了解参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中的位置等。


一个简单的利用反射获取类型信息的例子:

using system;
using sytem.reflection;
class reflecting 
{
       static void Main(string[]args)
       {
             reflecting reflect=new reflecting();//定义一个新的自身类
             //调用一个reflecting.exe程序集

             assembly myAssembly =assembly.loadfrom(“reflecting.exe”)
             reflect.getreflectioninfo(myAssembly);//获取反射信息
       }

       //定义一个获取反射内容的方法
       void getreflectioninfo(assembly myassembly)
       {
             type[] typearr=myassemby.Gettypes();//获取类型
             foreach (type type in typearr)//针对每个类型获取详细信息
            {
                   //获取类型的结构信息
                  constructorinfo[] myconstructors=type.GetConstructors;

                 //获取类型的字段信息
                 fieldinfo[] myfields=type.GetFiedls()

                 //获取方法信息
                 MethodInfo   myMethodInfo=type.GetMethods();

                 //获取属性信息
                 propertyInfo[] myproperties=type.GetProperties

                 //获取事件信息
                 EventInfo[] Myevents=type.GetEvents;
           }
      }
}
其它几种获取type对象的方法:
1、System.type   参数为字符串类型,该字符串必须指定类型的完整名称(包括其命名空间)
2、System.type 提供了两个实例方法:GetNestedType,GetNestedTypes
3、Syetem.Reflection.Assembly 类型提供的实例方法是:GetType,GetTypes,GetExporedTypes
4、System.Reflection.Moudle 提供了这些实例方法:GetType,GetTypes,FindTypes



这篇关于C#学习笔记之反射的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程