反射基础

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

先定义一个类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
    [Serializable]
    class Poster
    {
        
            private int count;
       
            public string OwnerName { get; set; }
       
            public string Title { get; set; }
           
            public string Content { get; set; }

            public void makePoster(string titile,string content) {
                Title = titile;
                Content = content;
            }
            public string getPoster() {               
                return  "Title: "+Title+" Content: " +Content +" postby"+OwnerName;
            }
            public string getPoster(string s) {
                return s;
            }
        
    }
}
 

1.获得Type对象

    • 通过字符串如:
       Type t1 = Type.GetType("ConsoleApplication2.Poster"); 
    • 具体的类:
      Type t2 = typeof(ConsoleApplication2.Poster);
    • 通过实例:
       Poster p = new Poster();
       Type t3 = p.GetType(); 

2.通过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为类名。

3.System.Reflection常用类

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

4.反射方法

参看3小结表列表中的MethodInfo,在调用方法时候注意是否重载,下面代码表示了在相同方法名称,不同签名的情况下方法的获取,一般来说如果只有一个方法只需使用

GetMethod(“方法的名称”)

private static void rec()
        {
            Poster pp = new Poster();

            Type p = Type.GetType("ConsoleApplication2.Poster");
            Console.WriteLine(p.ToString());

            Type p2 = typeof(ConsoleApplication2.Poster);
            Console.WriteLine(p2.ToString());

            Console.WriteLine(pp.GetType().ToString());

            string path = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "ConsoleApplication2.exe";
            Assembly ss = Assembly.LoadFrom(path);
            Console.WriteLine(ss.GetType("ConsoleApplication2.Poster").ToString() + "4");
            Module mod = ss.GetModules()[0];
            Console.WriteLine(mod.GetType("ConsoleApplication2.Poster").ToString() + "5");
            //以上是加载的各种方法

            MethodInfo[] methodList = p.GetMethods();
            foreach (MethodInfo info in methodList)
            {
                Console.WriteLine(info.ToString());//方法列表
            }
            object object_p = Activator.CreateInstance(p);//实例化一个对象
            Type[] types = new Type[1];
            types[0] = typeof(string);
            MethodInfo meth = p.GetMethod("getPoster", types);//获取getPoser(string s)方法
            object[] ob = new object[1];//传入参数
            ob[0] = "good dog";
            Console.WriteLine(meth.Invoke(object_p, ob));//调用该方法

            Type[] types2 = new Type[0];           
            MethodInfo meth2 = p.GetMethod("getPoster",types2);  //获取getPoster()方法          
            Console.WriteLine(meth2.Invoke(object_p,null));
        }

结果为如下

image

这里可以看到属性的生成方法。反射得到方法的列表默认是public的方法

5.反射属性

反射属性的获得是用列表中的PropertyInfo,和方法的书写格式基本一样,一个GetProperties针对枚举,一个是GetProperty针对个例,通过SetValue和GetValue进行赋值和取值

private static void rec2() {
            Type p = Type.GetType("ConsoleApplication2.Poster");//获得反射对象
            Console.WriteLine(p.ToString());
            PropertyInfo[] p_info = p.GetProperties();//获得反射的所有公开属性
            foreach (PropertyInfo p_in in p_info) {
                Console.WriteLine(p_in.ToString());
            }

            object p_r = Activator.CreateInstance(p);//创建一个实例
            PropertyInfo ptile = p.GetProperty("Title");//获得Title属性
            ptile.SetValue(p_r, "这个是主题");//设置属性值
           Console.WriteLine( ptile.GetValue(p_r).ToString());//获得属性值
        }
输出结果为
image

6.属性和方法的综合用例

为属性复制,取值,通过方法进行处理

private static void ATest() { 
            Type poster=typeof(ConsoleApplication2.Poster);
            object newposter = Activator.CreateInstance(poster);
            PropertyInfo p_owner = poster.GetProperty("OwnerName");
            p_owner.SetValue(newposter, " Keith");              //为用户赋值
            MethodInfo p_make = poster.GetMethod("makePoster");//获得makePoster方法
            object[] para = new object[2];
            para[0] = "天气";
            para[1] = "北京的天气最近真的很差啊";
            p_make.Invoke(newposter, para);//传入参数
            Type[] types = new Type[0];
            MethodInfo p_getposter = poster.GetMethod("getPoster", types);//获得getPoster方法注意和获得makePoster不同
            Console.WriteLine(p_getposter.Invoke(newposter,null));//输出
        }

输出结果

image

private static void setPosterRec() { 
            Dictionary<string,string> oneposter=new Dictionary<string,string>();
            oneposter.Add("OwnerName", "Keith");
            oneposter.Add("Title","回复Rita");
            oneposter.Add("Content","确实不怎么样这天气");
            Type poster = typeof(ConsoleApplication2.Poster);
            object newposter = Activator.CreateInstance(poster);
            PropertyInfo[] pinfo = poster.GetProperties();
            foreach (PropertyInfo p in pinfo) {
                if (oneposter.ContainsKey(p.Name)) {
                    p.SetValue(newposter, Convert.ChangeType(oneposter[p.Name], p.PropertyType));
                }
            }
            Poster thisp = newposter as Poster;
            Console.WriteLine(thisp.getPoster());
        }

7.模拟属性值批量赋值

private static void setPosterRec() { 
            Dictionary<string,string> oneposter=new Dictionary<string,string>();
            oneposter.Add("OwnerName", "Keith");
            oneposter.Add("Title","回复Rita");
            oneposter.Add("Content","确实不怎么样这天气");
            //假设数据来自xml
            Type poster = typeof(ConsoleApplication2.Poster);
            object newposter = Activator.CreateInstance(poster);
            PropertyInfo[] pinfo = poster.GetProperties();//获得属性列表
            foreach (PropertyInfo p in pinfo) {
                if (oneposter.ContainsKey(p.Name)) {//枚举属性列表根据属性的名字和字典key值相同的赋值
                    p.SetValue(newposter, Convert.ChangeType(oneposter[p.Name], p.PropertyType));
                    //Convert.ChangeType(oneposter[p.Name], p.PropertyType),根据反射的对象类型进行赋值
                }
            }
            Poster thisp = newposter as Poster;
            Console.WriteLine(thisp.getPoster());
        }

这段代码有一个关键的地方就是Convert.ChangeType(oneposter[p.Name], p.PropertyType) 这里将字典中value转换为类Poster中相同的类型,否则在赋值的时候会报错

输出结果

image

8.读取类中的字段和特性

Poster中有一个私有字段Count,我们来把他变化一下输出,特性的输出用GetCustomAttribute

private static void getprivateRec() {
            Type poster = typeof(ConsoleApplication2.Poster);
            object newposter = Activator.CreateInstance(poster);
            /*FieldInfo 读取类中的字段,第一个是字段名称,后面是枚举参数值,包括private和public*/
            FieldInfo file = poster.GetField("count",BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
            /*后面的和属性一样*/
            file.SetValue(newposter, 200);
            Console.WriteLine(file.GetValue(newposter));

            /*这里是输出特性*/
            object[] typeAttribute = poster.GetCustomAttributes(false);
            foreach (object a in typeAttribute) {
                Console.WriteLine(a.ToString());
            }
        }

输出结果

image

原文地址:https://www.cnblogs.com/keithmoring/p/4121156.html