反射基础

反射基础

  反射用于在程序运行过程中,获取类里面的信息或发现程序集并运行的一个过程。通过反射可以获得.dll和.exe后缀的程序集里面的信息。使用反射可以看到一个程序集内部的类,接口,字段,属性,方法,特性等信息。

一、各种GetType()、typeof的区别

  首先就是获取Tyoe对象的来源不同:

    class Program
    {
        static void Main(string[] args)
        {

            Type t1 = Type.GetType("ConsoleApplication2.Person");   //从字符串中获得Type对象
            Console.WriteLine(t1.ToString());

            Type t2 = typeof(ConsoleApplication2.Person);           //从具体类中获得Type对象
            Console.WriteLine(t2.ToString());

            Person p = new Person();
            Type t3 = p.GetType();              //实例,从实例中获得Type对象

            Assembly ass = Assembly.LoadFrom(@"C:\Users\Administrator\Desktop\ConsoleApplication2\ConsoleApplication2\bin\Debug\ConsoleApplication2.exe");
            Console.WriteLine(ass.GetType("ConsoleApplication2.Person").ToString());    //从字符串中获得Type对象
            Module mod = ass.GetModules()[0];
            Console.WriteLine(mod.GetType("ConsoleApplication2.Person").ToString());    //从字符串中获得Type对象

            Console.ReadKey();
        }
    }

  三者的区别在于typeof()和Type.GetType()是从一个类中获取对象,而Object.GetType()是从一个类的实例获得对象。

  而前两者的区别在于:

  • 只有typeof()是运算符。
  • Type.GetType()是实例方法。
  • Object.GetType()是基类System.Object的方法(无参数,在Type类、Assembly类、Module类中都有这个无参方法),实例方法。
  • Assembly.GetType() 、Module.GetType()、Type.Get()都是各自对象的实例方法。

在System.Reflection命名空间内包含多个反射常用的类,下面表格列出了常用的几个类。

类型 作用
Assembly 通过此类可以加载操纵一个程序集,并获取程序集内部信息
EventInfo 该类保存给定的事件信息
FieldInfo 该类保存给定的字段信息
MethodInfo 该类保存给定的方法信息
MemberInfo 该类是一个基类,它定义了EventInfo、FieldInfo、MethodInfo、PropertyInfo的多个公用行为
Module 该类可以使你能访问多个程序集中的给定模块
ParameterInfo 该类保存给定的参数信息
PropertyInfo 该类保存给定的属性信息

二、System.Reflection.Assembly类 

通过Assembly可以动态加载程序集,并查看程序集的内部信息,其中最常用的就是Load()这个方法。

Assembly assembly = Assembly.Load("MyAssembly");  

  注意在Assembly里面的加载程序集有3个方法,分别是Load、LoadFrom和LoadFile。这3个方法有什么异同呢?

  1、如果你引用了命名空间,那么就直接Load()方法,参数里面写上命名空间+类名就可以加载了。

  2、如果仅仅知道一个dll文件的那么就要用LoadFrom()方法了,参数里面直接填写完整的路径。

LoadFrom 方法具有以下缺点。请考虑改用 Load。

-如果已加载一个具有相同标识的程序集,则即使指定了不同的路径,LoadFrom 仍返回已加载的程序集。 
-如果用 LoadFrom 加载一个程序集,随后加载上下文中的一个程序集尝试加载具有相同显示名称的程序集,则加载尝试将失败。对程序集进行反序列化时,可能发生这种情况。

总结: LoadFrom只能用于加载不同标识的程序集, 也就是唯一的程序集, 不能用于加载标识相同但路径不同的程序集。

3、LoadFile (加载指定路径上的程序集文件的内容。)

  这个方法是从指定的文件来加载程序集,它是调用外部的API实现的加载方式,和上面Load,LoadFrom方法的不同之处是这个方法不会加载此程序集引用的其他程序集,也就是不会加载程序的依赖项。而同时也是不能加载相同标识的程序集的。

  利用Assembly的object CreateInstance(string)方法可以反射创建一个对象,参数0为类名。

    class Program
    {
        static void Main(string[] args)
        {
            Assembly assm = Assembly.Load("fanshe");
            Console.WriteLine(assm.FullName);   //输出 fanshe, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

            //注释上面两行,移除程序集的引用

            Assembly assm1 = Assembly.LoadFrom(@"D:\fanshe.dll");
            Console.WriteLine(assm1.FullName);      ////输出 fanshe, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

            //与Assembly.LoadFrom基本一样,只是如果被加载的dll,还依赖其他的dll的话,被依赖的对象不会加载
            Assembly assm2 = Assembly.LoadFile(@"D:\fanshe.dll");
            Console.WriteLine(assm2.FullName);

            Console.ReadKey();
        }
    }

三、System.Type类

Type是最常用到的类,它一般用于装载反射得到的类对象,通过Type可以得到一个类的内部信息,也可以通过它反射创建一个对象。一般有三个常用的方法可以得到Type对象。

1.利用typeof()得到Type对象

  Type type = typeof(Example);

2.利用System.Object.GetType()得到Type对象

  Example example = new Example();  Type type = example.GetType();

3.利用System.Type.GetType()得到Type对象

Type type = Type.GetType("MyAssembly.Example",false,true)   //注意0是类名,参数1表示若找不到对应类时是否抛出异常,参数2表示类名是否区分大小写

示例:

    public class Program
    {
        static void Main(string[] args)
        {
            Person p1 = new Person();

            Type t1 = typeof(Person);
            Type t2 = p1.GetType();

            Person p2 = Activator.CreateInstance(t1) as Person;
            Person p3 = Activator.CreateInstance(t2) as Person;

            Console.ReadKey();
        }
    }

    public class Person
    {
        public int Id { get; set; }

        public string Name { get; set; }
    }

四、反射方法

1.通过 Type.GetMethods()能查找到类里面的方法

代码示例:

    class Program
    {
        static void Main(string[] args)
        {
            Type t = typeof(Person);
            MethodInfo[] MethodInfoList = t.GetMethods();
            foreach (MethodInfo info in MethodInfoList)
            {
                Console.WriteLine(info);
            }

            Console.ReadKey();
        }
    }

  输出结果如下:

    

  留意到里面所有的方法,包括继承来的都列出来了。另外可以留意到,属性的读取设置,在根本上也是一个方法。

  调用反射得到的方法使用Invoke方法(),示例如下:

        static void Main(string[] args)
        {
            Assembly assembly = Assembly.Load("fanshe");
            Type type = assembly.GetType("fanshe.Person");      //注意要输入全部路径,包括命名空间
            object obj = Activator.CreateInstance(type);
            MethodInfo methodShow = type.GetMethod("Show");  //根据方法名获取MethodInfo对象
            //调用无参方法Show,Invoke表示执行方法
            methodShow.Invoke(obj, null);  // 参数1类型为object[],代表Hello World方法的对应参数,输入值为null代表没有参数

            Console.ReadKey();
        }

五、反射属性

  1、通过System.Reflection.Property能查找到类里面的属性
  常用的方法有GetValue(object,object[])获取属性值和SetValue(object,object,object[])设置属性值

  代码示例:

    class Program
    {
        static void Main(string[] args)
        {
            Type t = typeof(Person);
            PropertyInfo[] PropertyInfoList = t.GetProperties();
            foreach (PropertyInfo info in PropertyInfoList)
            {
                Console.WriteLine(info);
            }

            Console.ReadKey();
        }
    }

  输出结果如下:

    

  2、另外还可以通过PropertyInfo对象的GetValue和SetValue方法读取和设置创建出来的对象的属性值

    class Program
    {
        static void Main(string[] args)
        {
            Assembly assembly = Assembly.Load("fanshe");
            Type type = assembly.GetType("fanshe.Person");      //注意要输入全部路径,包括命名空间
            object obj = Activator.CreateInstance(type);

            //调用要属性的方法
            PropertyInfo propertyName = type.GetProperty("Name");    //获取Name属性对象
            propertyName.SetValue(obj, "张飞", null);                //设置Name属性的值

            object objName = propertyName.GetValue(obj, null);  //获取属性值
            Console.WriteLine(objName);     //输出张飞

            Console.ReadKey();
        }
    }  

  下面给出一个方法与属性的综合示例:

class Program
    {
        static void Main(string[] args)
        {
            Assembly assembly = Assembly.Load("fanshe");
            Type type = assembly.GetType("fanshe.Person");      //注意要输入全部路径,包括命名空间
            object obj = Activator.CreateInstance(type);
            MethodInfo methodShow = type.GetMethod("Show");  //根据方法名获取MethodInfo对象
            //调用无参方法Show,Invoke表示执行方法
            methodShow.Invoke(obj, null);  // 参数1类型为object[],代表Hello World方法的对应参数,输入值为null代表没有参数

            //调用带参数方法
            MethodInfo methodAdd = type.GetMethod("Add");
            object[] objArr = new object[]{1,2};
            methodAdd.Invoke(obj,objArr);   //输出3   第二个参数为传入去的参数列表

            //调用要属性的方法
             PropertyInfo propertyName = type.GetProperty("Name");    //获取Name属性对象
             propertyName.SetValue(obj,"张飞",null);                  //设置Name属性的值
             PropertyInfo propertyAge = type.GetProperty("Age");      //获取Age属性对象
             propertyAge.SetValue(obj, 24, null);                     //把Age属性设置为34
             MethodInfo methodSay = type.GetMethod("Say");
             methodSay.Invoke(obj,null);    //输出我的名字叫张飞,我今年24岁
             Console.ReadKey();
        }
    }

  Person类的代码如下:

namespace fanshe
{
    public class Person
    {
        private string name;
        public string Name  get { return name; } set { name = value; } }

        private int age;
        public int Age { get { return age; } set { age = value; } }

        public void Show() { Console.WriteLine("我是Person类里的Show方法!"); }

        public void Say() { Console.WriteLine("我的名字叫{0},我今年{1}岁", this.Name, this.Age); }

        public void Add(int i, int j) { Console.WriteLine(i + j); }
    }
}

  2、根据属性的类型设置属性的值:

  我们有可能会一次过设置很多属性的值,而这些属性里面可能有字符串类型、整型等等。因此,我们需要动态设置,这是需要根据属性的类型设置属性的值。

namespace DynamicSetValue
{
    class Program
    {
        static void Main(string[] args)
        {
            Type type = typeof(Person);      //注意要输入全部路径,包括命名空间
            object obj = Activator.CreateInstance(type);
            //假设这是存在于XML的数据
            Dictionary<string, string> dic = new Dictionary<string, string>();
            dic.Add("Id", "1");
            dic.Add("Name", "神灵武士");
            dic.Add("Birthday", "2001-01-01");

            PropertyInfo[] ProArr = type.GetProperties();
            foreach (PropertyInfo p in ProArr)
            {
                if (dic.Keys.Contains(p.Name))
                {
                    p.SetValue(obj, Convert.ChangeType(dic[p.Name], p.PropertyType), null);  //当需要给属性设置不同类型的值时
                }
            }

            Person person = obj as Person;
            Console.WriteLine(person.Birthday);

            Console.ReadKey();
        }
    }

    //有三种不同类型的属性
    public class Person
    {
        public int Id { get; set; }
        public string Name { get; set;}
        public DateTime Birthday { get; set; }
    }
}

  输出如下:

  

  如果不根据类型来转换,则报如下错误:

  

六、反射字段

  通过 System.Reflection.FieldInfo 能查找到类里面的字段

  它包括有两个常用方法SetValue(object ,object )和GetValue(object)  因为使用方法与反射属性非常相似。

    class Program
    {
        static void Main(string[] args)
        {
            Assembly assembly = Assembly.Load("fanshe");
            Type type = assembly.GetType("fanshe.Person");      //注意要输入全部路径,包括命名空间
            object obj = Activator.CreateInstance(type);

            //调用要属性的方法
            FieldInfo fieldName = type.GetField("name", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance);    //获取name字段对象,后面的那个枚举参数指定,非公开字段也搜索,即读取private的name字段
            fieldName.SetValue(obj, "张飞");           //设置name字段的值

            object objName = fieldName.GetValue(obj);  //获取属性值
            Console.WriteLine(objName);     //输出张飞

            Console.ReadKey();
        }
    }

七、反射特性

  通过System.Reflection.MemberInfo的GetCustomAttributes(Type,bool)就可反射出一个类里面的特性。

    class Program
    {
        static void Main(string[] args)
        {
            Assembly assembly = Assembly.Load("fanshe");
            Type type = assembly.GetType("fanshe.Person");      //注意要输入全部路径,包括命名空间
            object obj = Activator.CreateInstance(type);
            object[] typeAttributes=type.GetCustomAttributes(false);       //获取Person类的特性
            foreach (object attribute in typeAttributes)
            {
                Console.WriteLine(attribute.ToString());    //输出 System.SerializableAttribute   因为我在Person上里加了个[Serializable]
            }

            Console.ReadKey();
        }
    }

八、应用实例

  利用反射实现的简单工厂模式的多数据库系统实例:

  App.Config配置文件

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="DAL" value="ConsoleApplication2.Access"/>
  </appSettings>
</configuration>

  主程序代码:

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            Assembly assembly = Assembly.Load("ConsoleApplication2");
            string str = System.Configuration.ConfigurationManager.AppSettings["DAL"];
            Type type = assembly.GetType(str);      //注意真正实现的对象路径写在了外面,这样要更改数据库只要改配置文件,不需要改动程序!
            IDAL DAL = (IDAL)Activator.CreateInstance(type);
            DAL.Insert();

            Console.ReadKey();
        }
    }

    interface IDAL
    {
        void Insert();
    }

    class SqlServer : IDAL
    {
        public void Insert()
        {
            Console.WriteLine("SqlServer增加一条记录!");
        }
    }

    class Access : IDAL
    {
        public void Insert()
        {
            Console.WriteLine("Access增加一条记录!");
        }
    }
}

  2、不引用命名空间根据路径运行dll里的方法示例

  Person类代码:

namespace fanshe
{
    public class Person
    {
        private string name;
        public string Name
        { get { return name; }  { name = value; } }

        private int age;
        public int Age
        { get { return age; } set { age = value; } }

        public string Say(string str)
        {
            Console.WriteLine("我的名字叫{0},我今年{1}岁,另外你给我传入的参数是:{2}", this.Name, this.Age,str);
            return "返回结果";
        }
    }
}

  主程序代码:

    class Program
    {
        static void Main(string[] args)
        {
            //先把引用的命名空间移除
            Assembly assembly = Assembly.LoadFrom(@"D:\fanshe.dll");
            Type type = assembly.GetType("fanshe.Person");
            object obj = Activator.CreateInstance(type);
            PropertyInfo proName = type.GetProperty("Name");
            proName.SetValue(obj,"关羽",null);
            PropertyInfo proAge = type.GetProperty("Age");
            proAge.SetValue(obj,24,null);
            MethodInfo methodSay = type.GetMethod("Say");
            object[] objList = new object[1]{"传入测试参数"};
            object result = methodSay.Invoke(obj,objList);
            Console.WriteLine(result);  //输出 返回值

            Console.ReadKey();
        }
    }

  3、获得List<T>中的T类型:

List<Dog> dogs = new List<Dog>();
Type type = dogs.GetType();
if (type.IsGenericType)
{
  Type[] genericArgTypes = type.GetGenericArguments();
  if (genericArgTypes[0] == typeof(Dog))
  {
  //你想要判断的是这个吗?
  }
}

  当然,如果List<T>是你定义的泛型,那么直接typeof(T)更加简单。

时间: 2024-11-06 15:29:54

反射基础的相关文章

Java反射基础笔记

由于工作中发现自己的基础知识掌握的并不是很牢固,遇到的问题与学习的东西也没有很好的做过记录,导致再遇到时耗费大量时间上网搜索,所以决定串下基础知识并尽量形成记录,方便自己之后遗忘时查询,也方便各位有需求的伙伴翻阅查看,大家共同探讨.学习. 本次梳理的是Java反射的基础,该系列为笔者学习慕课反射讲解视频的学习笔记,尽可能全的记录,以帮助初学者快速掌握反射基础知识,如需转载该系列请注明原文链接. 一.反射之Class类的使用 二.反射之获取方法信息 更多编辑中 --

C#反射基础知识和实战应用

首先来说一下什么是反射? 反射提供了封装程序集.模块和类型的对象(Type类型) 可以使用反射动态的创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型,然后,可以调用类型的方法或访问其字段和属性 . 总之,有了反射,以前很多实现不了的功能都可以实现. 下面先来写一个小例子,体验一下反射是怎么一回事: 打开VS2010,新建一个控制台应用程序,在program.cs里面写代码 首先引入命名空间: using System.Reflection; 下如下代码: PropertyInfo l

java反射基础知识(一)

一.反射 反射:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制. 要想解剖一个类,必须先要获取到该类的字节码文件对象.而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.对于反射的操作实际上就是通过Class对象获取: *a.java.lang.reflect.Field:提供有关类或接口的单个

java反射基础知识(四)反射应用实践

反射基础 p.s: 本文需要读者对反射机制的API有一定程度的了解,如果之前没有接触过的话,建议先看一下官方文档的Quick Start. 在应用反射机制之前,首先我们先来看一下如何获取一个对象对应的反射类Class,在Java中我们有三种方法可以获取一个对象的反射类. 通过getClass方法 在Java中,每一个Object都有一个getClass()方法,通过getClass方法我们可以获取到这个对象对应的反射类: 1 2 String s = "ziwenxie"; Class

反射基础详解

附:本博文记录反射基础知识,扩展请参见反射目录下的其余博文. 一.通过一个对象获得完整的包名和类名 方法:class.getClass().getName(). 二.实例化Class类对象 有三种方法: 1.推荐此种方式进行构建类:Class.forName("className"); 2.java的任何一个java对象都有getClass方法; 3.每个类都有class属性:className.class. 一.二点详见示例代码: package temp.reflect; publ

[转]反射基础

反射用于在程序运行过程中,获取类里面的信息或发现程序集并运行的一个过程.通过反射可以获得.dll和.exe后缀的程序集里面的信息.使用反射可以看到一个程序集内部的类,接口,字段,属性,方法,特性等信息. 一.各种GetType().typeof的区别 首先就是获取Tyoe对象的来源不同: class Program { static void Main(string[] args) { Type t1 = Type.GetType("ConsoleApplication2.Person"

Java:反射基础

Java:反射基础 引入反射 反射是什么 能够动态分析类能力的程序称为反射. 反射是一种很强大且复杂的机制. Class类 在程序运行期间,Java运行时系统始终为所有对象维护一个被称为运行时的类型标识.这个对象追踪着每个对象所属的类.虚拟机利用运行时类的信息选择相应的方法执行. 我们可以通过专门的Java类访问这些信息.保存这些信息的类称为Class. 根据对象获得类信息: MyClass myClass; ..... Class cl = myClass.getClass(); cl.get

C#反射与特性(一):反射基础

目录 C#反射与特性(一):反射基础 1. 说明 1.1 关于反射.特性 2. 程序集操作 2.1 获取 程序集对象(Assembly) 2.2 Assembly 使用 2.3 获取程序集的方式 C#反射与特性(一):反射基础 1. 说明 1.1 关于反射.特性 在 <C# 7.0 本质论>中,关于这方面的知识在 <第十八章 反射.特性和动态编程>:在<C# 7.0 核心技术指南>中,这部分内容在<第19章 反射和元数据>. [图片来自 <C# 7.0

Java的反射基础技术

今天本人给大家讲解一下Java的反射基础技术,如有不对的或者讲的不好的可以多多提出,我会进行相应的更改,先提前感谢提出意见的各位了!!! 什么是反射? 反射它是根据字节码文件可以反射出类的信息.字段.方法.构造方法等内容,还可以创建对象调用方法,我们称它为反射技术. 如何通过反射获取Class对象了? 它有三种方法,1.通过Class获取类对象 2.通过getClass()方法获取类对象 3.通过Class.forName(“完整类名”)获取类对象 案例:通过反射获得Class对象的3种方法 C