博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
反射简介—类型反射和晚期绑定
阅读量:6943 次
发布时间:2019-06-27

本文共 8810 字,大约阅读时间需要 29 分钟。

使用元数据完整地描述类型(类、接口、结构、枚举和委托)的能力是.NET平台的一个关键要素,像对象序列化、WCF等技术都需要在运行时通过元数据来发现类型格式。

通过ildasm.exe这个工具,我们可以查看一个程序集的元数据(Ctrl+M组合键)。在.NET中,利用反射(reflection)服务,我们就可以通过编程方式得到与ildasm.exe显示的相同的元数据信息。

System.Reflection这个命名空间中包括了我们使用反射中涉及的所有类型,但是开始介绍反射之前,我们要看看System.Type类。

System.Type类

System.Type类定义了很多成员,用来检查某个类型的元数据,它们返回的类型大多数都是System.Reflection命名空间中的类型。例如说,Type.GetMethods()返回一个MethodInfo类型的数组,Type.GetFields()返回一个FieldInfo类型的数组等等。

在.NET中,有三种方式可以得到一个Type类的实例引用。

使用System.Object.GetType()

由于Type是一个抽象类,所以不能直接使用new关键字创建一个Type对象,我们的首选方案是使用System.Object定义的GetType()方法,这个方法返回一个表示当前对象元数据的Type类的实例。

Student stu = new Student();Type studentType = stu.GetType();

通过上面代码也可以看到,使用这个方法获得Type类的实例是有前提的,必须得到类型(Student类)的编译时信息,并且内存中要有该类新的实例(stu实例)。

使用typeof()

在程序中,我们还可以通过C#的typeof操作符类获取类型信息。

Type type = typeof(Student);

使用typeof操作符,我们就不需要先建立一个实例来提取类型信息,但是,仍然需要知道类型的编译时信息,因为typeof需要通过强类型来获取类型信息。

使用System.Type.GetType()

在.NET还有一种更灵活的方式得到类型信息,可以通过System.Type类的静态成员GetType(),然后指定类型的完全限定名。采用这种方法,我们可以不需要知道类型的编译时信息,GetType()方法可以接受任何字符串值(而不是强类型),当然这个字符串就是类型名字的字符串形式。

Type.GetType()方法有很多重载,我们介绍一个常用的:

public static Type GetType(string typeName, bool throwOnError, bool ignoreCase);

使用这个方法我们可以指定两个布尔类型的参数,一个用来表示当类型找不到时是否抛出异常,另一个用来表示是否区分字符串大小写,下面就是第三种获得Student的Type类实例的方式:

Type type = Type.GetType("ReflectionTest.Student", false, true);

在上面这个语句中,字符串没有包含类型所在的程序集信息,这种情况就被认为该类型是定义在当前执行的程序集中。当需要得到一个外部程序集的类型元数据时,字符串参数必须使用类型完全限定名,加上类型所在程序集的友好名字。

Type type = Type.GetType("ReflectionTest.Student, ReflectionTest", false, true);

反射

为了进一步了解反射以及System.Type,下面看一个例子,在这个例子中,我们将使用一些System.Reflection中常被用到的类型,例如MethodInfo,FieldInfo和PropertyInfo等。

namespace ReflectionTest{    class Student : IComparable    {        public int Id { get; private set; }        public string Name { get; set; }        public int Age { get; set; }        public string Gender { get; set; }        public void Greeting(string name)        {            Console.WriteLine("{0} said Hello to {1}", this.Name, name);        }        public string GetInfo()        {            return string.Format("Basic Info:\n Name: {0}\n Age: {1}\n Gender: {2}", this.Name, this.Age, this.Gender);        }        public int CompareTo(object obj)        {            throw new NotImplementedException();        }    }    class Program    {        static void Main(string[] args)        {            Type type = typeof(Student);            ListMethods(type);            ListFields(type);            ListProps(type);            ListInterfaces(type);            ListOhterStatus(type);            Console.Read();        }        static void ListMethods(Type t)        {            Console.WriteLine("------Methods------");            var methodInfos = from m in t.GetMethods()                              select m;            foreach (var methodInfo in methodInfos)            {                //Get return type                string retVal = methodInfo.ReturnType.FullName;                string paramInfo = "(";                // Get params                foreach (ParameterInfo pi in methodInfo.GetParameters())                {                    paramInfo += string.Format("{0} {1}", pi.ParameterType, pi.Name);                }                paramInfo += ")";                Console.WriteLine("-->{0} {1} {2}", retVal, methodInfo.Name, paramInfo);            }            Console.WriteLine();        }        static void ListFields(Type t)        {            Console.WriteLine("------Fields------");            var fieldNames = from f in t.GetFields()                             select f.Name;            foreach(var fieldName in fieldNames)                Console.WriteLine("-->{0}", fieldName);            Console.WriteLine();        }        static void ListProps(Type t)        {            Console.WriteLine("------Properties------");            var propNames = from p in t.GetProperties()                             select p.Name;            foreach (var propName in propNames)                Console.WriteLine("-->{0}", propName);            Console.WriteLine();        }        static void ListInterfaces(Type t)        {            Console.WriteLine("------Interfaces------");            var interfaceNames = from i in t.GetInterfaces()                             select i.Name;            foreach (var interfaceName in interfaceNames)                Console.WriteLine("-->{0}", interfaceName);            Console.WriteLine();        }        static void ListOhterStatus(Type t)        {            Console.WriteLine("------Other status------");            Console.WriteLine("Full name is: {0}", t.FullName);            Console.WriteLine("Base class is: {0}", t.BaseType);            Console.WriteLine("Is type abstract? {0}", t.IsAbstract);            Console.WriteLine("Is type a class type? {0}", t.IsClass);                        Console.WriteLine();        }    }}

例子还是比较简单的,我们通过反射查看了Student类型的一些元数据信息,结果如下,通过这个例子大致了解到反射的基本使用。

动态加载程序集

在程序的运行中,我们有时会需要加载外部程序集,这个操作被称作动态加载。

通过System.Reflection中的Assembly类型,我们就可以动态的加载程序集,并找到关于程序集的相关特性。通过Assembly类提供的Load()和LoadFrom()方法,我们可以通过代码动态的加载一个程序集。

注意,当使用Load()时,需要把要加载的程序集放到ExternalAssemblyTest工程的"bin/debug"目录,这样Load()方法才能找到这个程序集;当我们使用LoadFrom()方法的时候就比较灵活了,我们可以指定要加载程序集的绝对路径。下面看一个简单的例子:

namespace ExternalAssemblyTest{    class Program    {        static void Main(string[] args)        {            try            {                Assembly asm = Assembly.Load("ReflectionTest");                //Assembly asm = Assembly.LoadFrom(@"C:\WorkSpace\VS\SharpInDepth\ExternalAssemblyTest\bin\Debug\ReflectionTest.exe");                DisplayTypesInAssembly(asm);            }            catch (Exception e)            {                Console.WriteLine(e.Message);            }            Console.Read();        }        static void DisplayTypesInAssembly(Assembly asm)        {            Console.WriteLine("------Types in Assembly------");            Console.WriteLine("-->{0}", asm.FullName);            var types = from t in asm.GetTypes()                            select t;            foreach (var type in types)                Console.WriteLine("Type: {0}", type);        }    }}

通过这个例子我们可以到了两种动态加载程序集的方法调用,通过反射,我们可以查看被加载程序集的详细信息。

晚期绑定

晚期绑定(late binding)是一种创建一个给定类型的实例,并在运行时调用其成员,而不需要在编译时知道改类型存在的一种技术。

System.Activator类

System.Activator类是.NET晚期绑定过程中的一个关键类型。在代码中,可以通过System.Activator类的CreateInstance静态方法(CreateInstance有很多重载形式,下面给出了一个常用形式)来创建一个晚期绑定类型的实例。

public static object CreateInstance(Type type)

修改上面的例子,这次先动态加载程序集ReflectionTest,然后通过晚期绑定方式为程序集中的Student类型创建一个实例。

static void Main(string[] args){    try    {        Assembly asm = Assembly.Load("ReflectionTest");        //Assembly asm = Assembly.LoadFrom(@"C:\WorkSpace\VS\SharpInDepth\ExternalAssemblyTest\bin\Debug\ReflectionTest.exe");        //Get Student Type        Type studentType = asm.GetType("ReflectionTest.Student");        //Create Student object with late binding        object student = Activator.CreateInstance(studentType);        Console.WriteLine("Create object {0} with late binding", student);        ListMethods(studentType);    }    catch (Exception e)    {        Console.WriteLine(e.Message);    }    Console.Read();}static void ListMethods(Type t){    Console.WriteLine("------Methods------");    var methodInfos = from m in t.GetMethods()                      select m;    foreach (var methodInfo in methodInfos)    {        //Get return type        string retVal = methodInfo.ReturnType.FullName;        string paramInfo = "(";        // Get params        foreach (ParameterInfo pi in methodInfo.GetParameters())        {            paramInfo += string.Format("{0} {1}", pi.ParameterType, pi.Name);        }        paramInfo += ")";        Console.WriteLine("-->{0} {1} {2}", retVal, methodInfo.Name, paramInfo);    }    Console.WriteLine();}

代码的输出为

对于已经创建的student实例,我们可以通过反射来调用实例的方法。

调用没有参数的方法

根据前面了解到,当我们得到一个Type实例后,就可以通过Type.GetMethod()得到一个MethodInfo对象;然后就可以通过MethodInfo对象的Invoke()方法来实现一个方法的调用。

举例,可以在例子中加入下面代码来调用Student类型的GetInfo()方法,由于该方法不需要参数,所有Invoke()的时候用一个null。

//Get the methodMethodInfo getInfoMethod = studentType.GetMethod("GetInfo");//call the method without argument//null here shows that the method has no argumentstring info = (string)getInfoMethod.Invoke(student, null);Console.WriteLine(info);

代码的输出为,由于没有给Name和Gender属性设置值,所以这里显示为空。

调用有参数的方法

在使用晚期绑定调用需要参数的方法时,要将参数打包到一个object类型的数组中。

下面我们调用属性的set方法给属性赋值。

//Get the methodMethodInfo setName = studentType.GetMethod("set_Name");//call the method without argument//create object array with values of argumentssetName.Invoke(student, new object[] { "Wilber" });MethodInfo setAge = studentType.GetMethod("set_Age");setAge.Invoke(student, new object[] { 28 });MethodInfo setGender = studentType.GetMethod("set_Gender");setGender.Invoke(student, new object[] { "Male" });

程序输出为

总结

通过这篇文章,介绍了反射的一些基本概念,并且通过一些例子简单的演示了反射的使用。通过文章中所有的介绍,应该就可以对反射有个基本的认识了。

接下来一篇文章会介绍一下C#特性(Attribute)跟反射的结合使用。

转载地址:http://fsanl.baihongyu.com/

你可能感兴趣的文章
JVM(四)垃圾收集器_分代收集器
查看>>
根据图片路径生成二进制流,下载图片
查看>>
正则表达式的基本用法
查看>>
Shell 同步时间脚本
查看>>
条目十五《注意strng实现的多样性》
查看>>
P1387 最大正方形
查看>>
mysql distinct 用法详解及优化
查看>>
transform,
查看>>
好一朵美丽的spacer,
查看>>
json,
查看>>
一:Redis的7个应用场景
查看>>
开启vue源码的解析攻略---认识flow
查看>>
Promise源码深入理解
查看>>
acm 2031
查看>>
【HDOJ】4080 Stammering Aliens
查看>>
Sublime的Package Control的安装
查看>>
从一到二:利用mnist训练集生成的caffemodel对mnist测试集与自己手写的数字进行测试...
查看>>
Spark集群 + Akka + Kafka + Scala 开发(4) : 开发一个Kafka + Spark的应用
查看>>
HDU1078 FatMouse and Cheese(记忆化搜索)
查看>>
Codeforces Round #437 C. Ordering Pizza
查看>>