反射
- .Net提供的关于反射的内容都包含在System.Reflection/System.Type…命名空间下
DLL的组成
编译完成之后DLL实际包含2个部分IL代码Metadata
IL代码
.class public auto ansi serializable beforefieldinit System.Object { .custom instance void System.Runtime.InteropServices.ClassInterfaceAttribute::.ctor(valuetype System.Runtime.InteropServices.ClassInterfaceType) = ( 01 00 02 00 00 00 00 00 ) .custom instance void System.Runtime.InteropServices.ComVisibleAttribute::.ctor(bool) = ( 01 00 01 00 00 ) .custom instance void __DynamicallyInvokableAttribute::.ctor() = ( 01 00 00 00 ) }
Metadata
- DLL名称
- 接口名称
- 命名空间
- 类名称
- …
上面这些都是IL组成的Metadata,它用来描述Dll的构成部分
反射.Net框架提供给我读取DLL等类库中Metadata(编译高级语言所产生的IL和Metadata)的信息.
反射加载DLL
满足CLI(Common Language Infrastructure)(通用语言基础架构)的DLL,都可以使用反射加载.
- 托管与非托管
有GC的一般为托管代码,没有GC机制一般为非托管代码.
正常方法(Common非反射)
添加应用
创建实例调用
//创建实例 调用 DBHelper dbHelper = new DBHelper(); dbHelper.Id = 1; dbHelper.Name = "仗劍走天涯"; dbHelper.Query();
通过反射加载DLL
//1 动态加载 默认加载当前路径的dll文件,不需要后缀
Assembly assembly = Assembly.Load("Ruanmou.DB.Sqlserver");
// 必须是完整路径
Assembly assembly1 = Assembly.LoadFile(@"E:\online7\20160928Advanced7Course2Reflection\MyReflection\MyReflection\bin\Debug\Ruanmou.DB.Sqlserver.dll");//LoadFile不会加载依赖项,必须是完整路径
// 可以是当前路径 也可以是完整路径
Assembly assembly2 = Assembly.LoadFrom("Ruanmou.DB.Sqlserver.dll");
Assembly.LoadFrom(@"E:\online7\20160928Advanced7Course2Reflection\MyReflection\MyReflection\bin\Debug\Ruanmou.DB.Sqlserver.dll");
读取module、类、方法、特性
foreach (var item in assembly.GetModules())
{
//一般为DLL的全路径名称??
//当多个类库编译为一个DLL文件时,一个类库就会时一个模块
Console.WriteLine(item.FullyQualifiedName);
}
foreach (var item in assembly.GetTypes())
{
//获得下面所有的类型
Console.WriteLine(item.FullName);
}
Type typeDBHelper = assembly.GetType("Ruanmou.DB.Sqlserver.DBHelper");//2 获取类型 (获取类型信息的方式不止一个)
foreach (var item in typeDBHelper.GetConstructors())
{
//获取构造函数的名称,构造函数没有名称,统一都是".ctor"
Console.WriteLine(item.Name);
}
foreach (var item in typeDBHelper.GetProperties())
{
Console.WriteLine(item.Name);
}
foreach (var item in typeDBHelper.GetFields())//字段
{
//相对于属性,没有getset访问器
Console.WriteLine(item.Name);
}
foreach (var item in typeDBHelper.GetAttributes())
{
//获取特性
Console.WriteLine(item.Name);
}
foreach (var item in typeDBHelper.GetMethods())
{
//包含父类的方法,包含属性的get set
Console.WriteLine(item.Name);
}
通过反射创建对象实例
通过反射创建对象
//创建引用DLL
Assembly assembly = Assembly.Load("Ruanmou.DB.Sqlserver");
//获取类型 当然还有很多别的方法
Type typeDBHelper = assembly.GetType("Ruanmou.DB.Sqlserver.DBHelper")
//创建对象
object oDBHelper = Activator.CreateInstance(typeDBHelper);
//创建不是调用默认构造函数的类
Activator.CreateInstance(typeTest, "demon");//调用带有一个string类型的构造函数
Activator.CreateInstance(typeTest, 11, "限量版(397-限量版)");//类似上面的
//调用
//可以使用强制类型转换直接调用,但是这失去了反射加载DLL的意义
DBHelper oDBHelperSqlserver = (DBHelper)oDBHelper;//因为通常情况下,不知道类型是什么样的,也无法动态加载
oDBHelperSqlserver.Query();
//源程序和DLL实现了相同的接口,则可以实现面向接口的通信,可以实现动态加载
IDBHelper dbHelperReflection = (IDBHelper)oDBHelper;
dbHelperReflection.Query();
反射+简单工厂+配置文件==(IOC依赖注入/控制反转)
虽然再上述场景中可以实现动态的调用,但是再实际的生产环境中仍然面临者不够灵活的缺点
//IDBHelper接口
public interface IDBHelper
{
void Query();
}
//创建一个简单工厂模式,封装DLL的调用
public class SimpleFactory
{
//读取配置文件
private static string IDBHelperConfig = ConfigurationManager.AppSettings["IDBHelper"];
public static IDBHelper CreateDBHelper()
{
//解析配置文件的内容
string dllName = IDBHelperConfig.Split(',')[1];
string className = IDBHelperConfig.Split(',')[0];
//封装加载DLL和创建DB实例
Assembly assembly = Assembly.Load(dllName);
Type type = assembly.GetType(className);
object oObject = Activator.CreateInstance(type);
return (IDBHelper)oObject;
}
}
<!--AppConfig内容-->
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<appSettings>
<add key="IDBHelper" value="Ruanmou.DB.Sqlserver.DBHelper,Ruanmou.DB.Sqlserver"/>
</appSettings>
</configuration>
//调用 实现
IDBHelper dbHelperFactory = SimpleFactory.CreateDBHelper();
dbHelperFactory.Query();
//再上述的过程中 实现了DB类库的动态加载
//再需要新增,修改等业务场景中无需修改代码,即可实现修改业务逻辑的操作
通过反射破坏单例*
即可以调用类的私有的构造函数
//一个带有私有构造函数的类 一个简单的单例模式的类
/// <summary>
/// 单例模式
/// </summary>
public sealed class Singleton
{
private Singleton()
{
Console.WriteLine("初始化一次");
}
private static Singleton Instance = new Singleton();
public static Singleton CreateInstance()
{
return Instance;
}
}
//调用
Type typeSingleton = assembly.GetType("Ruanmou.DB.Sqlserver.Singleton");
//第二个参数为Bool nonpublic,设置为True 可以调用私有的构造函数
Activator.CreateInstance(typeSingleton, true);
通过反射创建泛型类
//再名称中需要指定泛型中的占位符 `1--一个参数 `3--3个参数
Type typeGeneric = assembly.GetType("Ruanmou.DB.Sqlserver.GenericClass`2");
//创建实例之前,需要指定创建实例的参数类型,该方法会返回一个修改过后的类型
typeGeneric = typeGeneric.MakeGenericType(typeof(int),typeof(string));
//需要以修改过后的类型来创建实例
Activator.CreateInstance(typeGeneric);
通过反射调用方法
//创建一个类有各种方法可被调用
public class ReflectionTest
{
public int Id { get; set; }
public string Name { get; set; }
public string Field = null;
public static string FieldStatic = null;
#region 构造函数
public ReflectionTest()
{
Console.WriteLine("这里是{0}无参数构造函数", this.GetType());
}
public ReflectionTest(string name)
{
Console.WriteLine("这里是{0} 有参数构造函数", this.GetType());
}
public ReflectionTest(int id, string name)
{
Console.WriteLine("这里是{0} 有参数构造函数", this.GetType());
}
#endregion
public static void ShowStatic(string name)
{
Console.WriteLine("这里是{0}的ShowStatic", typeof(ReflectionTest));
}
public void ShowGeneric<T>(string name)
{
Console.WriteLine("这里是{0}的ShowStatic T={1}", this.GetType(), typeof(T));
}
public void Show1()
{
Console.WriteLine("这里是{0}的Show1", this.GetType());
}
public void Show2(int id)
{
Console.WriteLine("这里是{0}的Show2", this.GetType());
}
public void Show3()
{
Console.WriteLine("这里是{0}的Show3_1", this.GetType());
}
public void Show3(int id, string name)
{
Console.WriteLine("这里是{0}的Show3", this.GetType());
}
public void Show3(string name, int id)
{
Console.WriteLine("这里是{0}的Show3_2", this.GetType());
}
public void Show3(int id)
{
Console.WriteLine("这里是{0}的Show3_3", this.GetType());
}
public void Show3(string name)
{
Console.WriteLine("这里是{0}的Show3_4", this.GetType());
}
private void Show4(string name)
{
Console.WriteLine("这里是{0}的Show4", this.GetType());
}
}
反射调用构造方法
Type typeTest = assembly.GetType("Ruanmou.DB.Sqlserver.ReflectionTest");
object oTest = Activator.CreateInstance(typeTest);
Activator.CreateInstance(typeTest, "demon");
Activator.CreateInstance(typeTest, 11, "限量版(397-限量版)");
Activator.CreateInstance(typeTest,new object {"demon"});
//系统CreateInstance方法
public static object CreateInstance(Type type, params object[] args);
反射调用实例方法/静态方法/私有方法/泛型方法(MVC控制器采用的模式)
Type typeTest = assembly.GetType("Ruanmou.DB.Sqlserver.ReflectionTest");
//创建对象的实例
object oTest = Activator.CreateInstance(typeTest);
foreach (var item in typeTest.GetMethods())
{
Console.WriteLine(item.Name);
}
{
//调用无参数的方法
MethodInfo method = typeTest.GetMethod("Show1");
method.Invoke(oTest, null);
}
{
//调用带一个int参数的方法
MethodInfo method = typeTest.GetMethod("Show2");
method.Invoke(oTest, new object[] { 11 });
}
{
//调用静态方法
MethodInfo method = typeTest.GetMethod("ShowStatic");
//调用静态方法的时候可以不传递实例
method.Invoke(null, new object[] { "KOBE→Bryant" });
}
//调用重载方法需要指定参数类型,给GetMethod方法传递一个Type数组
{
//调用无参数的重载方法
MethodInfo method = typeTest.GetMethod("Show3", new Type[] { });
method.Invoke(oTest, null);
}
//调用又参数的重载方法,类型需要一致
{
MethodInfo method = typeTest.GetMethod("Show3", new Type[] { typeof(int) });
method.Invoke(oTest, new object[] { 11 });
}
{
MethodInfo method = typeTest.GetMethod("Show3", new Type[] { typeof(string) });
method.Invoke(oTest, new object[] { "限量版(397-限量版)" });
}
{
MethodInfo method = typeTest.GetMethod("Show3", new Type[] { typeof(string), typeof(i
method.Invoke(oTest, new object[] { "书呆熊@拜仁", 22 });
}
{
MethodInfo method = typeTest.GetMethod("Show3", new Type[] { typeof(int), typeof(stri
method.Invoke(oTest, new object[] { 33, "不懂微软" });
}
//调用私有方法,需要传递一个枚举类型BindingFlags 的Instance和.NonPublic
{
MethodInfo method = typeTest.GetMethod("Show4", BindingFlags.Instance | BindingFlags.NonPublic)
method.Invoke(oTest, new object[] { "有木有" });
}
//调用泛型方法,和调用泛型实例类似,需要指定泛型的的类型;(不需要占位符)
{
MethodInfo method = typeTest.GetMethod("ShowGeneric");
method = method.MakeGenericMethod(typeof(string));
method.Invoke(oTest, new object[] { "有木有" });
}
反射字段和属性,分别获取值/设置值
//用于反射的类
public class People
{
public People()
{
Console.WriteLine("{0}被创建", this.GetType().FullName);
}
public int Id { get; set; }
public string Name { get; set; }
}
//创建实例
Type typeTest = assembly.GetType("Ruanmou.DB.Sqlserver.ReflectionTest");
object oTest = Activator.CreateInstance(typeTest);
//foreach (var item in typeTest.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public))
//{
// Console.WriteLine(item.Name);
//}
foreach (var prop in typeTest.GetProperties())//获取所有的属性
{
Console.WriteLine(prop.GetValue(oTest));//PropertyInfo.GetValue(实例) 获取属性值
Console.WriteLine(prop.Name);
if (prop.Name.Equals("Id"))
{
prop.SetValue(oTest, 22);//PropertyInfo.SetValue(实例,值的Object对象) 设置属性值
}
else if (prop.Name.Equals("Name"))
{
prop.SetValue(oTest, "Bond(331-object)");
}
//PropertyInfo.Propertytype 返回属性的类型
Console.WriteLine(prop.GetValue(oTest));
}
//反射读取属性,设置属性的意义(灵活,简洁)
Using(SqlConnection sqlconn = new SqlConnection(connstring))
{
SqlCommand cmd = new SqlCommand(querystring,sqlconn);
sqlconn.Open();
DataReader reader = cmd.ExcuteDataReader();
User oUser = new User();
Type type = typeof(oUser);
if(reader.Read())
{
foreach(var item in type.GetProperties())
{
//简介的从Sql查询中赋值
//当然还可以加入依赖关系(极简ORM)
item.SetValue(oUser,reader[item.Name]);
}
}
}
反射的好处和局限
- 动态灵活
- 代码复杂,性能损耗(大约是普通方法的几倍到几十倍,百万次大约几十毫秒级)