程序运行图解:
理解反射之前,先理解下程序的执行过程,如下图所示:
程序员写了高级语言(c#、vb等等),经过编译器编译后,生成DLL或者EXE文件,文件经过JIT编译成机器码,最终被计算机执行。
如果再细分的话,DLL/EXE文件中包含了元数据和中间语言,中间语言经过JIT编译成机器码。
元数据可以理解为数据清单,当CLR执行DLL/EXE时,先去查找元数据,元数据中描述了DLL/EXE的详细信息,比如包含了哪些命名空间,命名空间中有哪些类,类中有哪些方法,有哪些属性。
反射概念:
反射Reflection:System.Reflection,是.Net Framework提供的一个帮助类库,可以读取并使用meatdata(元数据)。
反射初体验:
在.net代码中,如果要加载一个自定义的类库,则需要三部:第一步项目添加类库的引用,第二步是代码中using 类库的命名空间,第三步才是调用类库的方法。
如果要用反射来实现这个步骤,则不需要添加引用的步骤,直接就可以将程序集加载到内存中,并可以读取里面的信息。如下实例所示:
自定义一个接口:
namespace DB.Interface
{
/// <summary>
/// 数据访问类
/// </summary>
public interface IDBHelper
{
void Query();
}
}
自定义一个MySql的类:
using DB.Interface;
using System;
namespace DB.MySql
{
public class MySqlHelper : IDBHelper
{
public void Query()
{
Console.WriteLine("{0}.Query", this.GetType().Name);
}
public MySqlHelper()
{
Console.WriteLine("{0}被构造", this.GetType().Name);
}
}
}
程序的主方法:
using DB.MySql;
using System;
using System.Reflection;
namespace MyReflection
{
class Program
{
static void Main(string[] args)
{
try
{
Console.WriteLine("***********************常规用法************************");
MySqlHelper mySqlHelper = new MySqlHelper();
mySqlHelper.Query();
Console.WriteLine("***********************反射用法************************");
//将dll加载到内存中
Assembly assembly = Assembly.Load("DB.MySql");//程序集的名称
foreach (var type in assembly.GetTypes())
{
Console.WriteLine("第一次循环:" + type.Name);
foreach (var method in type.GetMethods())
{
Console.WriteLine(method.Name);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.Read();
}
}
}
程序使用常规的写法和反射的写法,分别打印出实例的名称和方法的名称,运行结果如下图:
都打印出了MySqlHelper这个类名,还有Query方法名。
反射实例中还打印出了ToString等方法名称,其实是Object的方法。
反射用途:
反射的作用不仅是可以查看程序集中内容,它还可以使用程序集,也就是可以调用方法等。
Type type = assembly.GetType("DB.MySql.MySqlHelper");//获取类型
dynamic dDBHelper= Activator.CreateInstance(type);//创建对象
dDBHelper.Query();//调用对象的方法
执行结果如图:
可以像常规的引用方式一样,调用实例中的方法。
步骤可以分为:1.获取程序集类型 2.创建对象 3.调用对象方法
这样看上去不是很简便,可以对反射的相关方法做一个封装。
using DB.Interface;
using System;
using System.Reflection;
using System.Configuration;
namespace MyReflection
{
/// <summary>
/// 定义一个工厂
/// </summary>
public class SimpleFactory
{
//static关键字表示'静态',它的作用是当程序第一次调用类的时候完成初始化,就执行一次,而且只会执行一次。
private static string IDBHelperConfig = ConfigurationManager.AppSettings["IDBHelperConfig"];
private static string DllName = IDBHelperConfig.Split(',')[0];
private static string TypeName = IDBHelperConfig.Split(',')[1];
public static IDBHelper CreateInstance()
{
Assembly assembly = Assembly.Load(DllName);//程序集的名称
Type type = assembly.GetType(TypeName);//获取类型
object oDBHelper = Activator.CreateInstance(type);//创建对象
IDBHelper iDBHelper = oDBHelper as IDBHelper;
return iDBHelper;
}
}
}
需要在App.config文件中做一个配置:
<appSettings>
<add key="IDBHelperConfig" value="DB.MySql,DB.MySql.MySqlHelper"/>
</appSettings>
这样做的原因是方便修改引用的程序集名称。
调用的地方就可以这样写:
IDBHelper dBHelper = SimpleFactory.CreateInstance();
dBHelper.Query();
执行结果仍然为:
总结:
反射的意义在于它是可以动态地加载程序集,实现对程序的扩展。
以上内容都是个人理解,可能有不对或不完整的地方。