反射简介—类型反射和晚期绑定

使用元数据完整地描述类型(类、接口、结构、枚举和委托)的能力是.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 method
MethodInfo getInfoMethod = studentType.GetMethod("GetInfo");
//call the method without argument
//null here shows that the method has no argument
string info = (string)getInfoMethod.Invoke(student, null);
Console.WriteLine(info);

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

调用有参数的方法

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

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

//Get the method
MethodInfo setName = studentType.GetMethod("set_Name");
//call the method without argument
//create object array with values of arguments
setName.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)跟反射的结合使用。

时间: 2024-10-07 23:11:22

反射简介—类型反射和晚期绑定的相关文章

c#反射机制学习和利用反射获取类型信息

反射(Reflection)是.NET中的重要机制,通过放射,可以在运行时获得.NET中每一个类型(包括类.结构.委托.接口和枚举等)的成员,包括方法.属性.事件,以及构造函数等.还可以获得每个成员的名称.限定符和参数等.有了反射,即可对每一个类型了如指掌.如果获得了构造函数的信息,即可直接创建对象,即使这个对象的类型在编译时还不知道 1..NET可执行应用程序结构 程序代码在编译后生成可执行的应用,我们首先要了解这种可执行应用程序的结构. 应用程序结构分为应用程序域—程序集—模块—类型—成员几

Java反射简介

Java反射简介 1.Class类 1) 在面向对象的世界里,万事万物皆对象.(java语言中,静态的成员.普通数据类型除外) 类是不是对象呢?类是(哪个类的对象呢?)谁的对象呢? 类是对象,类是java.lang.Class类的实例对象 2)这个对象到底如何表示 3 )Class.forName("类的全称") 不仅表示了,类的类类型,还代表了动态加载类 请大家区分编译.运行 编译时刻加载类是静态加载类.运行时刻加载类是动态加载类 4)基本的数据类型 void关键字 都存在类类型 5

c#使用反射调用类型成员示例

在实际的工作中直接使用反射的机会比较少,有印象的就是一次自己做的WinForms小工具的时候利用反射来动态获取窗体上的每个控件,并且为必要的控件动态添加注册事件.因为刚入职新公司,为了更快的了解公司的业务.和开发习惯,先和现在公司同事一起修改现有系统的一些小Bug.在Tester提交的Bug中有一个是对GridView进行动态的排序——点击一个列时使用该列作为条件进行排序(PS:点击一个列时前台会将该列的字符串(该字符串是)传到后台的方法中). 使用反射的原因 为什么会选择使用反射呢?在项目中我

反射机制,反射的性能,如何优化?

反射机制的定义: 是在运行状态中,对于任意的一个类,都能够知道这个类的所有属性和方法,对任意一个对象都能够通过反射机制调用一个类的任意方法,这种动态获取类信息及动态调用类对象方法的功能称为java的反射机制. 反射的作用: 1.动态地创建类的实例,将类绑定到现有的对象中,或从现有的对象中获取类型. 2.应用程序需要在运行时从某个特定的程序集中载入一个特定的类.

java 反射和暴力反射 两个DEMO

该类为反射函数 获取和暴力获取ReflectPoin类中的属性 package com.tuozou.test; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class ReflectTest { public ReflectTest() { // TODO Auto-generated con

Java 反射机制[Field反射]

Java 反射机制[Field反射] 1.  反射概念及功能 反射就是把Java类中的各种成分映射成相应的Java类.例如一个Java类中用一个Class类的对象来表示.一个类中的组成部分分为成员变量,方法,构造方法,包等等. Java反射机制主要提供了以下功能: 判断在运行时任意一个对象所属的类:在运行时构造任意一个类的对象:判断在运行时任意一个类所具有的成员变量和方法:在运行时调用任意一个对象的方法:生成动态代理. 2.  Field反射 以下代码将obj对象中的String类型的字段对应的

Java 反射机制[Method反射]

Java 反射机制[Method反射] 接着上一篇Java 反射机制[Field反射],通过调用Person类的setName方法将obj的name字段的Value设置为"callPersonSetNameMethod"来了解什么是Method反射.示例代码很简单,很容易理解. 可以看到Method.invoke()实际上并不是自己实现的反射调用逻辑,而是委托给sun.reflect.MethodAccessor来处理. 真正的反射是调用MethodAccessor.invoke()真

反射(2)使用反射

这一篇文章来总结下怎么使用反射的. 加载程序集 要加载程序集,可以调用 Assembly的LoadXXX系列方法. 1,Assembly.Load方法 1 //1,从GAC或应用程序基目录加载程序集 2 var assembly = Assembly.Load("ReflectionDemo.A, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");//或ReflectionDemo.A也可以 需要注意的是: 1)查找顺序,首

生成跨语言的类型声明和接口绑定的工具(Djinni )

Djinni 是一个用来生成跨语言的类型声明和接口绑定的工具,主要用于 C++ 和 Java 以及 Objective-C 间的互通. 示例接口定义文件: 1 # Multi-line comments can be added here. This comment will be propagated 2 # to each generated definition. 3 my_enum = enum { 4 option1; 5 option2; 6 option3; 7 } 8 9 my_