zoukankan      html  css  js  c++  java
  • C#反射

    在前面简单学习了Linq To Object的常用标准查询运算符,在我们项目开发中,运用Linq表达式方便了我们编程,而接下来要讲的另一个内容——反射,也能很好地帮助我们处理某些特殊的情况。

    一、反射的概念: 

    反射提供了封装程序集、模块和类型的对象(Type类型)。可以使用反射动态创建类型的实例,将类型绑定到现有对象,或从现有对象获取类型并调用其方法或访问其字段和属性。如果代码中使用了属性,可以利用反射对它们进行访问。

    一般使用:工厂类,通过反射创建类的实例,实现层与层之间的解耦:  据层→数据会话层→业务逻辑层。 其中,数据会话层通过反射创建数据层的实例,业务逻辑层调用。

    二、反射Type中的四个函数:

                Person person = new Person();
                Student student = new Student();
                //判断指定的两个成员是否存在继承关系(判断一个类是否可用赋值给另一个类)    --后者继承于前者
                bool b1 = typeof(Person).IsAssignableFrom(typeof(Student));    //student继承了person
                bool b3 = person.GetType().IsAssignableFrom(student.GetType());//另一种写法
                bool b2 = typeof(Itest).IsAssignableFrom(typeof(Teacher));
    
                //判断是否是指定类的实例
                bool b4 = typeof(Person).IsInstanceOfType(student);     //结果为true   student继承了person
                bool b5 = person.GetType().IsInstanceOfType(student);//另一种写法   GetType当前对象的实例
    
                //判断是否是某个类的子类,非接口
                bool b6 = typeof(Student).IsSubclassOf(typeof(Person));
    
                //判断是否是抽象
                if (typeof(Drive).IsAbstract)
                {
                    Console.WriteLine("是抽象的");
                }
                else
                {
                    Console.WriteLine("不是抽象的");
                }
                Console.ReadKey();

    要使用到的类和接口:

        class Person{}
        class Student:Person{}
        class Teacher:Itest{}
        interface Itest{}
        public abstract class Drive{}

     

    三、反射中常用类的使用:

    需求:通过反射获得Common程序集中的成员,并使用成员。

    Comman程序集:

        public class FileCommon
        {
            public string Name { get; set; }
            public int Age { get; set; }
            public char Gender { get; set; }
            public FileCommon(string name)
            {
                this.Name = name;
            }
            public void WriteData(string path,string content)
            {
                File.WriteAllText(path, content, Encoding.Default);
            }
        }

    1、把Common.dll放到该应用程序的bin/Debug目录下

    string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"Common.dll");
    Assembly ass = Assembly.LoadFile(path);  //需要绝对路径  LoadFile加载路径程序集内容        -->也不一定要在debug目录下,自己构建绝对路径也可
    Type[] types = ass.GetTypes();             //获得加载到的程序集中的所有数据类型,包括公开和不公开的    
    //Type[] types = ass.GetExportedTypes(); //获取加载到的公开程序集中的数据

    2、可以遍历程序集中的类型,获取类型的命名空间和类型名称

    foreach (Type item in types)
    { 
        Console.WriteLine(item.Name);             //类型名称  --也就是common下类或者接口的名称
        Console.WriteLine(item.Namespace);        //命名空间
    }

    3、获取指定程序集中的数据类型

                //获得指定程序集中数据类型,包括公开的和不公开的
                Type type = ass.GetType("Common.FileCommon");
                Console.WriteLine(type.Name);
                Console.WriteLine(type.Namespace);

    4、创建type对象

          a:创建没有构造函数的对象

    object o = ass.CreateInstance("Common.FileCommon");     //FileCommom为Common命名空间的一个类 → 命名空间.类名 

        b:反射出来的类型有构造函数

     object o2 = Activator.CreateInstance(type, "参数");

    注意: 当反射出来的类型如果有构造函数,用上面代码中 ass.CreateInstance则会出现错误,如果有构造函数,那么该如何知道构造函数的参数?

    ConstructorInfo [] info = type.GetConstructors();    //查询所有的构造函数,可以看到构造函数需要传递参数的参数类型

    5、获得数据类型中所有的属性

                //获得数据类型中所有的属性
                PropertyInfo[] pinfo = type.GetProperties();   //获取属性  然后可以遍历
                foreach (PropertyInfo item in pinfo)
                {
                    Console.WriteLine(item.Name);
                }

    6、获得数据类型中所有的函数

                //获得数据类型中所有的方法函数
                MethodInfo[] minfo = type.GetMethods();   //获取所有的函数
                foreach (MethodInfo item in minfo)
                {
                    Console.WriteLine(item.Name);
                }

    7、目的:调用函数

                //调用WriteData函数
                MethodInfo method = type.GetMethod("WriteData");    //该类中的writeData方法
                object o3 = method.Invoke(o2, new object[] { "2.txt", "通过反射调用哒" });
                Console.WriteLine("调用成功");
                Console.ReadKey();

    四、小结: 

    当获得一个 .dll程序集的时候,需要先获取所有的类型,也就是这个程序集中的类,然后根据所需要类的名称,去创建指定名称的对象,如:Type type = ass.GetType("Common.FileCommon");  ,然后利用type获取构造函数、所有函数、属性(还有一些可以 type.方法名来获取相应的需求),再选择创建type对象的方法(构造或无构造),然后根据获得的函数来调用,传入相应的参数。

    五、具体应用例子:

    使用反射制作关机插件

    1、先定义关机接口和具体关机的插件方法:

        public interface IPlugin
        {
            string Name { get; }
            //负责关闭计算机
            void GuanJi(int t);
        }
        public class ShutDownClass : IPlugin
        {
            public void GuanJi(int t)
            {
                //关机
                ShutDown(t.ToString());
                //返回一个K类型的默认值
            }
    
            public void ShutDown(string second)
            {
                Process process = new Process();
                process.StartInfo.FileName = "cmd.exe";
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.RedirectStandardInput = true;
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.RedirectStandardError = true;
                process.StartInfo.CreateNoWindow = true;
                process.Start();
                process.StandardInput.WriteLine("shutdown -s -f -t " + second);
                process.StandardInput.WriteLine("exit");
    
                process.Close();
                process.Dispose();
            }
            public string Name
            {
                get { return "关机"; }
            }
        }

    2、窗体调用:

            private void Form1_Load(object sender, EventArgs e)
            {
                string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "plug");   //bin/debug/plug存放.dll文件
    
                string[] files = Directory.GetFiles(path);
                foreach (string item in files)
                {
                    Assembly ass = Assembly.LoadFile(item);
                    Type[] types = ass.GetExportedTypes();
                    for (int i = 0; i < types.Length; i++)
                    {
                        //判断是否为接口中的类,并不是抽象类
                        if (typeof(IPlugin).IsAssignableFrom(types[i]) && !types[i].IsAbstract)
                        {
                            //创建对象
                            o = Activator.CreateInstance(types[i]);
                            //获得指定对象
                            PropertyInfo pif = types[i].GetProperty("Name");
                            object o2 = pif.GetValue(o);
                            //添加到菜单栏
                            ToolStripItem tsi = PluginToolStripMenuItem.DropDownItems.Add(o2.ToString());
                            //把数据对象存储到要使用的对象
                            tsi.Tag = types[i];
                            //给添加的tsi注册单击事件
                            tsi.Click += (s, e2) =>
                            {
                                Type t = tsi.Tag as Type;
                                MethodInfo mi = t.GetMethod("GuanJi");
                                mi.Invoke(o, new object[] { 3000 });
                            };
                        }
                    }
                }
            }

    六、总结: 

    很多.dll文件可以通过反射的方式来获取相应的类,类的方法属性的使用,一些应用程序也可以通过反编译软件来获取一些方法属性等。

  • 相关阅读:
    hadoop-处理小文件
    hadoop 文件合并
    hadoop multipleoutputs
    超酷的 Vim 搜索技巧
    linux中DHCP服务配置文件/etc/dhcpd.conf详细说明
    cobbler启动问题
    MYSQL 5.5.32的单机多实例部署
    自动化运维之Cobbler自动化部署安装操作系统
    运维自动化之Cobbler系统安装使用详解[good]
    Cobbler自动部署主机系统
  • 原文地址:https://www.cnblogs.com/jiechou/p/9249015.html
Copyright © 2011-2022 走看看