为什么要使用反射

前言

废话不多说,“反射” 有哪些用途呢?根据我目前接触到的,总结了下面 3 种常见的反射应用场景:

1,动态加载对象。
2,赋予应用程序装载插件的能力。
3,创建对象并给对象属性赋值。

下面按照顺序,举例说明这 3 种反射场景。

一,动态加载对象

这里以动态加载应用程序窗体为例。假如有个按钮,用户点击按钮后,弹出其他窗体,常规代码如下:

using System;
using System.Windows.Forms;

namespace Reflect_Test
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        
        //点击按钮,Form2 弹出
        private void button1_Click(object sender, EventArgs e)
        {
            Form2 form2 = new Form2();
            form2.Show();
        }
    }
}

如果有个需求,按下按钮,弹出 Form3,而不是 Form3,怎么办。最最直接的办法就是把代码改一下,Form2 改成 Form3 就行了。这样是可以,但是你不得重新编译代码然后发布啊,万一哪天客户改变主意了,说还是 Form2 好看好用,要改回 Form2 ,怎么办,再改代码吗重新编译发布?会被折腾死吧。

进阶方案,弄一个 ini 配置文件,做一个配置,这样就不用修改代码了,代码如下:

public partial class Form1 : Form
{
   //配置管理器
   IniManagement iniManagement = new IniManagement();
   public Form1()
   {
        InitializeComponent();
   }

   private void button1_Click(object sender, EventArgs e)
   {
       Form form = new Form();

       string selectedForm = iniManagement.IniReadValue("Form", "SelectedForm");
       switch (selectedForm)
       {
           case "Form2": form = new Form2(); break;
           case "Form3": form = new Form3(); break;
           /*Other case...*/
           default: ; break;
       }

       form.Show();
   }
}

这样一来只需要修改下配置文件,就能实现窗体切换。那么问题来了,如果客户财大气粗,往桌子上拍了一沓软妹币,说,再做个 Form4 看看效果呢。gg,只能增加一个 case,这段代码又要改了,我们的 switch 只能用来对付已知的可能。

这个时候,就要抛弃 switch,使用反射,做个可配置化的升级版,代码如下:

public partial class Form1 : Form
{
    //配置管理器
    IniManagement iniManagement = new IniManagement();
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        Form form = new Form();

        //读取配置文件
        //string formConfig = iniManagement.IniReadValue("Form", "SelectedForm");

        //为了便于观察,这里直接写出配置文件的配置
        string formConfig = @"D:\Reflect Test\Form4\bin\Debug\Form4.dll,Form4.Form4";
        string[] dllClassPair = formConfig.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

        form = ReflectTools.CreateInstance(dllClassPair[0], dllClassPair[1]) as Form;
        form.Show();
    }
}

public static class ReflectTools
{
     public static Object CreateInstance(string appPathName, string className)
     {
         //Form4 的 dll 的位置
         if (!File.Exists(appPathName)) return null;
         //加载dll
         Assembly assembly = Assembly.LoadFrom(appPathName);
         if (assembly == null) return null;

         try
         {
             //反射出窗体对象
             //className: 程序集.类
             return assembly.CreateInstance(className);
         }
         catch
         {
             return null;
         }
      }
}

这样,我们就可以把编写好的 Form4.dll,丢在指定的目录里,再配置下配置文件就可以了。代码会从 dll 里反射出 Form4. 以后再来什么Form5、Form6、XXX,这段代码都是不用动的。其他需要动态加载的对象都可以通过这样的方式进行。

二、赋予应用程序装载插件的能力

 

发表评论