反射

学习反射,要知道什么是反射?为什么要用到反射?怎么用反射?

一、什么是反射?(What)

反射就是动态发现类型信息的能力。它帮助程序设计人员在程序运行时利用一些信息去动态地使用类型,这些信息在设计时是未知的,这种能力类型于后期绑定。反射还支持的更高级的行为,能在运行时动态创建新类型,并且对这些新类型的操作进行调用。

(反射就是通过某种事物去反射另一种事物;我们小时候进行喜欢用镜子对着墙上照,会出现一个个小光圈,这就是很典型的反射例子,我们把它理解成计算机里的反射就是我用某种对象去反射我无法直接获取到的东西。)。

二、为什么要用到反射?(Why)

动态调用dll,比如这个dll有一个类,我们不知道它里面有什么属性、方法、元素。但通过反射就可以知道它里的属性、方法,就可以实现调用。通过反射获取它的类型是什么。
适用场景:
              需要访问程序元数据的属性;

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

              在运行时构建新类型,使用 System.Reflection.Emit 中的类;

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

三、怎么用反射?(How)

运行时类型标识 

  • 运行时类型标识(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();

        }

一些在反射中经常使用的类

Assembly类

      Assembly类是可重用、无版本冲突并且可自我描述的公共语言运行库应用程序构造块。可以使用Assembly.Load和Assembly.LoadFrom方法动态地加载程序集。

Type类

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

Activator类

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

Binder类

      Binder类是一个用于执行类型转换的绑定器,Type对象的InvokeMember方法接受Binder对象,这个对象描述了如何将传递给InvokeMember的参数转换成方法实际需要的类型。
      Binder类是一个抽象类,要创建绑定器,需要重写方法BindToMethod、BindToField、SelectMehtod、SelectProperty和ChangeType。

DefaultMemberAttribute类

    DefaultMemberAttribute类用于类型并带有一个指明默认成员名称的字符串参数。能够通过InvokeMember调用默认成员,而不需要传递调用成员的名称。当需要绑定器但不需要特别的绑定行为时就可以使用它。

类定义:
using System;
using System.Collections.Generic;
using System.Text;
namespace ReflectionSample
{
    /**//**//**//// 
    /// 说明:一个简单的类
    public class ClassSample
    {
        // 默认构造
        public ClassSample()
        {
            this.name = "您调用了默认构造创建了一个类实例。";
        }
        // 带参构造
        public ClassSample(string name)
        {
            this.name = name;
        }
        // 字段 
        public string name;
        public string Field;
        // 属性
        private string property;
        public string Property
        {
            set { this.property = value; }
            get { return property; }
        }
        // public方法 
        public string PublicClassMethod()
        {
            return string.Format("您反射了一个Public方法");
        }
        // private方法
        private string PrivateClassMethod()
        {
            return string.Format("您反射了一个Private方法");
        }
        // static方法
        public static string StaticMethod()
        {
            return "您反射了一个Static方法";
        }
        // 帶參方法
        public string ParameterMethod(string para)
        {
            return para;
        }
        public event EventHandler eventHandler;
        public void DoEvent()
        {
            eventHandler(null,EventArgs.Empty);
        }
    }
}
反射示例
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Reflection;
using ReflectionSample;
/**//**//**//// 
/// 说明:一个简单的使用反射示例
public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        string path = Server.MapPath(Request.Path);
        string filePath = path.Substring(0, path.LastIndexOf(''//'')) + @"/bin/ReflectionSample.dll";
        // 获取程序集
        Assembly classSampleAssembly = Assembly.LoadFrom(filePath);
        // 从程序集中获取指定对象类型
        Type classSampleType = classSampleAssembly.GetType("ReflectionSample.ClassSample");
        // 使用Activator创建一个实例
        // 通过对象类型创建对象实例
        ClassSample s1 = Activator.CreateInstance(classSampleType) as ClassSample;
        Response.Write(s1.name + "(使用Activator创建一个实例)");
        // 动态调用构造函数
        // 调用无参构造
        ConstructorInfo studentConstructor1 = classSampleType.GetConstructor(new Type[] { });
        ClassSample s2 = studentConstructor1.Invoke(new object[] { }) as ClassSample;
        Response.Write(s2.name + "");
        // 调用有参构造
        ConstructorInfo studentConstructor2 = classSampleType.GetConstructor(new Type[] { typeof(string) });
        ClassSample s3 = studentConstructor2.Invoke(new object[] { "您调用了有参构造创建了一个类实例。" }) as ClassSample;
        Response.Write(s3.name + "");
        // 反射方法
        // 调用非静态方法
        string returnValue1 = classSampleType.InvokeMember("PublicClassMethod", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, s1, new object[] { }) as string;
        Response.Write(returnValue1 + "");
        // 调用静态方法
        string returnValue2 = classSampleType.InvokeMember("StaticMethod", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static, null, s1, new object[] { }) as string;
        Response.Write(returnValue2 + "");
        // 调用私有方法
        string returnValue3 = classSampleType.InvokeMember("PrivateClassMethod", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, s1, new object[] { }) as string;
        Response.Write(returnValue3 + "");
        // 反射参数
        MethodInfo parameterMethod = classSampleType.GetMethod("ParameterMethod");
        ParameterInfo[] paras = parameterMethod.GetParameters();
        for (int i = 0; i ", new object[] { paras[i].Name, paras[i].ParameterType.ToString(), paras[i].IsOptional.ToString(), paras[i].Position.ToString(), paras[i].DefaultValue.ToString() }));
        // 反射属性
        classSampleType.InvokeMember("Property", BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance, null, s1, new object[] { "您反射了一个属性" });
        string returnValue4 = classSampleType.InvokeMember("Property", BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance, null, s1, new object[] { }) as string;
        Response.Write(returnValue4 + "");
        // 反射字段
        classSampleType.InvokeMember("Field", BindingFlags.SetField | BindingFlags.Public | BindingFlags.Instance, null, s1, new object[] { "您反射了一个字段" });
        string returnValue5 = classSampleType.InvokeMember("Field", BindingFlags.GetField | BindingFlags.Public | BindingFlags.Instance, null, s1, new object[] { }) as string;
        Response.Write(returnValue5 + "");
    }
}
  • 性能影响
     反射的性能损失主要来源于比较类型、遍历成员、调用成员三种情形,其中比较类型耗时最小,调用成员耗时最多,所以尽量减少采用成员动态调用等反射方式可以提高应用程序性能。除此之外,采取后期绑定、避免将反射方法放到循环内产生放大效应等办法均可提升反射性能。(正常的程序都是编译的时候就获取到了类信息等,反射是动态调用的时候才获取到)。
 
原文地址:https://www.cnblogs.com/janneystory/p/5616136.html