反射用法

摘自:http://www.cnblogs.com/zhaopei/p/reflection.html#autoid-5-0

我们平时用反射主要做:

  • 获取类型的相关信息
  • 动态调用方法
  • 动态构造对象
  • 从程序集中获得类型。

获取类型的相关信息

反射的核心Type类,Type对象提供的属性和方法可以获取对象的一切信息,如:方法、字段、属性、事件...等等。

我们获取已加载程序集中类型的Type对象的几种方法:(以StringBuilder 类型为例)

  1. 直接使用typeof操作符 Type T1 = typeof(StringBuilder); 
  2. 通过类型实例 Type T2 = new StringBuilder().GetType(); 
  3. 通过Type类的静态方法 Type T3 = Type.GetType("System.IO.Stream"); 

不管使用那种,我们最终得到的结果都是一样的。

代码:

//获取类型本身信息
            var t = typeof(StringBuilder);
            Console.WriteLine("命名空间:{0}", t.Namespace);
            Console.WriteLine("直接基类型:{0}", t.BaseType);
            Console.WriteLine("全名:{0}", t.FullName);
            Console.WriteLine("是否是抽象类:{0}", t.IsAbstract);
            Console.WriteLine("是否为类:{0}", t.IsClass);

运行结果:

获取类型成员信息

    public class TClass
    {
        public string age { get; set; }
        public string sex = "female";
        public string GetNumber()
        {
            return sex;
        }
    }

       //获取内部成员信息(方法,属性,字段等)
           Type t1 = typeof(TClass);
            var members = t1.GetMembers();
            foreach (var m in members)
            {
                Console.WriteLine(m.MemberType.ToString() + ":" + m.Name);
            }

结果:

我们发现获取Type对象的成员大多都是以 isxxx、Getxxx、Getxxxs格式的。

isxxx格式的基本上都是判断是否是某类型。

动态调用方法

首先定义一个类:

    public class TClass_2
    {
        public void fun1(string str)
        {
            Console.WriteLine("fun1方法:{0}", str);
        }
        public void fun2()
        {
            Console.WriteLine("fun2方法");
        }
        public static void fun3()
        {
            Console.WriteLine("fun3是静态方法");
        }
    }
调用带参实例方法
            //调用带参数的实例方法
            var t2 = typeof(TClass_2);
            t2.InvokeMember("fun1", BindingFlags.InvokeMethod, null, new TClass_2(), new string[] { "test" });
调用无参实例方法
//调用无参实例方法
t2.InvokeMember("fun2", BindingFlags.InvokeMethod, null, new TClass_2(), null);
调用静态方法
//调用静态方法
 t2.InvokeMember("fun3", BindingFlags.InvokeMethod, null, t2, null);

我们来说下这几个参数的意思吧。

第一个:要被动态调用的方法名。

第二个:是一个枚举,表示是调用一个方法

第三个:是Binder,传的是null,使用默认值。

第四个:传如实例对象(调用实例方法时)或者Type对象(调用静态方法时)。

第五个:要传给被调用发的参数数组。

或者使用如下方式调用:

            //动态调用方法(方式2)
            t2.GetMethod("fun1", BindingFlags.Instance | BindingFlags.Public).Invoke(new TClass_2(), new string[] { "test" });
            t2.GetMethod("fun2", BindingFlags.Instance | BindingFlags.Public).Invoke(new TClass_2(), null);
            t2.GetMethod("fun3", BindingFlags.Static | BindingFlags.Public).Invoke(t2, null);

运行结果:

全动态调用

上面的两种方式,在编写代码的时候总是要先确定了已知的对象名和方法名。那么我们在不知道对象和方法名的时候是否也可以调用呢?答案是肯定的,实现如下:

            //全动态调用
            Console.WriteLine("请输入类名称");
            string className = Console.ReadLine();
            Console.WriteLine("请输入要执行的名称");
            string funName = Console.ReadLine();
            var t3 = Type.GetType(className);
            ConstructorInfo ci = t3.GetConstructors()[0];//获取构造函数
            var obj = ci.Invoke(null);
            t3.InvokeMember(funName, BindingFlags.InvokeMethod, null, obj, new string[] { "全动态调用" });

注意:这里要输入命名空间,否则报错

结果:

动态构造对象

同样,先定义一个类:

    public class TClass_3
    {
        public TClass_3()
        {
            Console.WriteLine("执行无参构造函数");
        }
        public TClass_3(string s)
        {
            Console.WriteLine("执行带单构造方法:{0}",s);
        }
    }
            //动态构造对象:方式一:
            Assembly asm = Assembly.GetExecutingAssembly();
            TClass_3 t4 = (TClass_3)asm.CreateInstance("ReflectionStudy.TClass_3", true);

            //动态构造对象,方式二:
            ObjectHandle oh = Activator.CreateInstance(null, "ReflectionStudy.TClass_3");

            //动态构造对象,方式三(构造有参构造函数)
            Assembly asm2 = Assembly.GetExecutingAssembly();
            TClass_3 t5 = (TClass_3)asm2.CreateInstance("ReflectionStudy.TClass_3", true,BindingFlags.Default,null,new string[]{"我是带参构造函数"},null,null);

结果:

从程序集中获得类型

取得当前代码所在程序集
Assembly ass = Assembly.GetExecutingAssembly();
Console.WriteLine("当前所在程序集名:"+ass.ManifestModule.Name);
Console.WriteLine("当前所在程序集路径:"+ass.Location);
通过反射加载程序集并创建程序中的类型对象
Assembly asm = Assembly.LoadFrom("Demo.dll");
Assembly asm = Assembly.Load("Demo");

区别

Assembly asm = Assembly.LoadFrom("net.exe");//需要加后缀,可以指定路径,如下面的
Assembly asm1 = Assembly.LoadFrom(@"C:\01文件\05Svn\BlogsCode\Blogs\Blogs.Web\bin\Blogs.BLL.dll");

Assembly asm2 = Assembly.Load("Blogs.BLL");//无需加后缀,不可以指定路径
//Assembly asm3 = Assembly.Load(@"C:\01文件\05Svn\BlogsCode\Blogs\Blogs.Web\bin\Blogs.BLL");//这里会报错
//使用Load可以加载当前程序bin目录行下的程序集或者系统程序集

//这里TClass可以是一个接口,那么可以在外面的dll任意实现了。  
TClass obj = (TClass)asm2.CreateInstance("net.tclass", true);//true:不区分大小写
obj.fun();//***调用动态加载的dll中的方法***

 这样带来的功能是非常强大的。如 我们在没有引用程序集的情况下,也可以使用到程序外的程序集。我们还可以根据不同情况引用不同的程序集。我们甚至还可以通过配置文件来直接配置代码运行时应该加载哪个dll,运行哪个dll中的哪个实现方法。

 

posted @ 2016-10-09 17:02  天空的一片云  阅读(148)  评论(0编辑  收藏  举报