Reflect(反射)
反射、反射,程序员的快乐。反射是无处不在的。
那么什么是反射:通过反射,可以在运行时获得程序或程序集中每一个类型(包括类、结构、委托、接口和枚举等)的成员和成员的信息。有了反射,即可对每一个类型了如指掌
简单来说,一般来说,我们通过编译生成一个dll文件,dll文件由IL+metadata组成,而我们的反射主要就是调用一些metadata(元数据)。
我们通过ILSpy反编译可以清楚的看到
现在我们新建几个类库和接口:
1、新建一个IDBHelper接口
public interface IDBHelper
{
void Query();
}
2、在建一个DBHelper类并实现这个接口。
public class DBHelper : IDBHelper
{
public int Id { get; set; }
public string Name { get; set; } public DBHelper()
{
//Console.WriteLine("这里是{0}的无参构造函数", this.GetType().FullName);
} public void Query()
{
//Console.WriteLine("这里是{0}的Query", this.GetType().FullName);
} }
当我们使用普通方法是调用方法时。
static void Main(string[] args)
{ #region Common
DBHelper dbHelper = new DBHelper();
dbHelper.Query();
#endregion
}
首先我们了解下反射是怎么用的,
1、动态加载Dll
2、获取类型信息
3、反射创建对象
#region 反射加载dll,读取module、类、方法、特性
Assembly assembly = Assembly.Load("DB.Sqlserver");//1 动态加载 默认加载当前路径的dll文件,不需要后缀
Assembly assembly1 = Assembly.LoadFile(@"D:\ASP.NetAdvanced\20170118Advanced8Course2Reflection\20170118Advanced8Course2Reflection\MyReflection7\MyReflection\bin\Debug\DB.Interface.dll");// 必须是完整路径
Assembly assembly2 = Assembly.LoadFrom("DB.Sqlserver.dll");// 可以是当前路径 也可以是完整路径 Console.WriteLine("************GetModules**********");
foreach (var item in assembly.GetModules())//获取模块信息
{
Console.WriteLine(item.FullyQualifiedName);
}
foreach (var item in assembly.GetTypes())//获取类型
{
Console.WriteLine(item.FullName);
}
Type typeDBHelper = assembly.GetType("DB.Sqlserver.DBHelper");//2 获取类型 (获取类型信息的方式不止一个)
foreach (var item in typeDBHelper.GetConstructors())//构造方法
{
Console.WriteLine(item.Name);
}
foreach (var item in typeDBHelper.GetProperties())//属性
{
Console.WriteLine(item.Name);
}
foreach (var item in typeDBHelper.GetMethods())//方法
{
Console.WriteLine(item.Name);
}
foreach (var item in typeDBHelper.GetFields())//字段
{
Console.WriteLine(item.Name);
} #endregion
下面有两种创建对象:一种直接创建,一种通过简单工厂+配置文件创建
一:
object oDBHelper = Activator.CreateInstance(typeDBHelper);//3 创建对象
IDBHelper dbHelperReflection = (IDBHelper)oDBHelper;
dbHelperReflection.Query();
二:
public class SimpleFactor
{
private static string IDBHelperConfig = ConfigurationManager.AppSettings["IDBHelper"];
private static string dllName = IDBHelperConfig.Split(',')[1];
private static string className = IDBHelperConfig.Split(',')[0];
public static IDBHelper CreateDBHelper()
{ Assembly assembly = Assembly.Load(dllName);
Type type = assembly.GetType(className);
object oObject = Activator.CreateInstance(type);
return (IDBHelper)oObject; }
}
IDBHelper dbHelperFactory = SimpleFactory.CreateDBHelper();
dbHelperFactory.Query();
接下来就细细说下如何具体的使用泛型。
首先我们创建一个反射测试类ReflectionTest:包含一些字段和重载方法。
/// <summary>
/// 反射测试类
/// </summary>
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("DB.Sqlserver.ReflectionTest");//2 获取类型 (获取类型信息的方式不止一个)
foreach (var item in typeTest.GetConstructors())
{
Console.WriteLine(item.Name);
}
Activator.CreateInstance(typeTest);//默认创建无参数构造函数
Activator.CreateInstance(typeTest, "demon");//带有string参数类型构造函数
Activator.CreateInstance(typeTest, 11, "限量版(397-限量版)"); //获取泛型方法,泛型方法和普通方法不同的是,获取类型时要输入全称,而泛型方法是会声明占位符,所以输入全称时一定要加占位符有几个,占位符:`
Type typeGeneric = assembly.GetType("Ruanmou.DB.Sqlserver.GenericClass`1");
//声明泛型类型
typeGeneric = typeGeneric.MakeGenericType(typeof(int));
Activator.CreateInstance(typeGeneric);
反射调用实例方法、静态方法、重载方法 选修:调用私有方法 调用泛型方法。
#region 反射调用实例方法、静态方法、重载方法 选修:调用私有方法 调用泛型方法
{
Console.WriteLine("**************反射调用实例方法****************");
Type typeTest = assembly.GetType("DB.Sqlserver.ReflectionTest");//2 获取类型 (获取类型信息的方式不止一个)
object oTest = Activator.CreateInstance(typeTest); foreach (var item in typeTest.GetMethods())
{
Console.WriteLine(item.Name);
}
{
MethodInfo method = typeTest.GetMethod("Show1");
method.Invoke(oTest, null);//调用show1无参数方法
}
{
MethodInfo method = typeTest.GetMethod("Show2");
method.Invoke(oTest, new object[] { 11 });//调用show2int参数方法
}
{
MethodInfo method = typeTest.GetMethod("ShowStatic");
method.Invoke(null, new object[] { "KOBE→Bryant" });//调用showstatic静态方法
}
{
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(int) });
method.Invoke(oTest, new object[] { "书呆熊@拜仁", 22 });
}
{
MethodInfo method = typeTest.GetMethod("Show3", new Type[] { typeof(int), typeof(string) });
method.Invoke(oTest, new object[] { 33, "不懂微软" });
}
{
MethodInfo method = typeTest.GetMethod("Show4", BindingFlags.Instance | BindingFlags.NonPublic);//获取show4静态方法
method.Invoke(oTest, new object[] { "有木有" });
}
{
MethodInfo method = typeTest.GetMethod("ShowGeneric");
method = method.MakeGenericMethod(typeof(string));//声明泛型方法参数类型
method.Invoke(oTest, new object[] { "有木有" });
}
}
#endregion
那么如何通过反射字段和属性,分别获取值和设置值
#region 反射字段和属性,分别获取值和设置值
{
Console.WriteLine("**************反射字段和属性****************");
//普通形式的设置
ReflectionTest test = new ReflectionTest();
test.Id = 11;
test.Name = "为";
//泛型形式设置
Type typeTest = assembly.GetType("DB.Sqlserver.ReflectionTest");
object oTest = Activator.CreateInstance(typeTest);
foreach (var prop in typeTest.GetProperties())
{
Console.WriteLine(prop.GetValue(oTest));
Console.WriteLine(prop.Name);
if (prop.Name.Equals("Id"))
{
prop.SetValue(oTest, 22);
}
else if (prop.Name.Equals("Name"))
{
prop.SetValue(oTest, "ming");
} Console.WriteLine(prop.GetValue(oTest));
}
} #endregion
也许大家觉得这样设置字段还不如直接声明对象设置,这样通过反射设置字段的最主要目的是达到一个动态设置的目的。
举个例子; 假设数据库中有张用户表,我们需要把用户表中的字段动态的添加到字段中,这个时候就是需要通过反射来实现这个功能。
下面就简单的实现这个功能的部分代码
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
using (SqlCommand comm = new SqlCommand(sql.ToString(), conn))
{
using (SqlDataReader read = comm.ExecuteReader())
{
while (read.Read())
{
T model = new T();
foreach (PropertyInfo item in propertyInfos)
{
if (!(read[item.Name] is DB))
item.SetValue(model, read[item.Name]);//关键
}
results.Add(model);
}
}
}
}
说了这么多,那么泛型究竟有哪些好处和缺点呢。
其实说白一点泛型就好比X射线,我们肉眼可以看到的方法都可以通过反射来调用。反射最主要的功能就是实现了方法的动态,达到了程序可拓展的目的。
那么缺点呢,缺点吗,就是出现错误不易察觉。当我们通过new一个对象时,如果new的对象不对,编译器马上就会报错,而反射就是越过了编译器,比如动态加载DllAssembly assembly = Assembly.Load("DB.Sqlserver");//1 动态加载
如果你dll名称都写错了,编译器是不会察觉的,那后面错误了都不知道在哪。
有人可能会担心泛型的一个效率的问题,下面做了一个简单的测试代码。
#region
{
Stopwatch watch = new Stopwatch();
watch.Start();
for (int i = 0; i < 1000000; i++)
{
DBHelper dbHelper = new DBHelper();
dbHelper.Id = 1;
dbHelper.Name = "yy";
dbHelper.Query();
}
watch.Stop();
Console.WriteLine("普通方式花费{0}ms", watch.ElapsedMilliseconds);
}
{
Assembly assemblyTest = Assembly.Load("DB.Sqlserver"); Type typeTest = assemblyTest.GetType("DB.Sqlserver.DBHelper"); Stopwatch watch = new Stopwatch();
watch.Start();
for (int i = 0; i < 1000000; i++)
{ object oTest = Activator.CreateInstance(typeTest); foreach (var prop in typeTest.GetProperties())
{
if (prop.Name.Equals("Id"))
{
prop.SetValue(oTest, 1);
}
else if (prop.Name.Equals("Name"))
{
prop.SetValue(oTest, "yy");
}
}
MethodInfo method = typeTest.GetMethod("Query");
method.Invoke(oTest, null);
}
watch.Stop();
Console.WriteLine("反射方式花费{0}ms", watch.ElapsedMilliseconds);
}
#endregion
方法重复一百万次反射需要的时间2秒不到,而且它们的绝对比误差会更低,所以说反射效率是不成问题的,除非你的程序效率已经精确到毫秒那就另当别论了。
Reflect(反射)的更多相关文章
- JAVA 构造器, extends[继承], implements[实现], Interface[接口], reflect[反射], clone[克隆], final, static, abstrac
记录一下: 构造器[构造函数]: 在java中如果用户编写类的时候没有提供构造函数,那么编译器会自动提供一个默认构造函数.它会把所有的实例字段设置为默认值:所有的数字变量初始化为0;所有的布尔变量设置 ...
- golang:reflect反射
因为之前一直以C++为主要开发语言,所以刚接触go语言中的reflect时感觉很懵逼,因此决定找资料彻底学习一下. 到底反射是什么? https://blog.golang.org/laws-of-r ...
- java reflect反射调用方法invoke
类定义 package Reflect; public class MyTest { public int a; public static int b; public static final in ...
- Java reflect 反射学习笔记
1. class 类的使用 万事万物皆对象 (基本数据类型, 静态成员不是面向对象), 所以我们创建的每一个类都是对象, 即类本身是java.lang.Class类的实例对象, 但是这些对象不需要 n ...
- golang基础--reflect反射
反射的知识点比较晦涩,后期会对此知识点展开深入的分析及示例代码展示 反射可达大提高程序的灵活性,使得inferface{}有更大的发挥余地 反射使用TypeOf和ValueOf函数从接口中获取目标对象 ...
- Golang reflect 反射
反射的规则如下: 从接口值到反射对象的反射 从反射对象到接口值的反射 为了修改反射对象,其值必须可设置 -------------------------------------------- ...
- GO_09:GO语言基础之reflect反射
反射reflection 1. 反射可以大大的提高程序的灵活性,使得 interface{} 有更大的发挥余地 2. 反射使用 TypeOf 和 ValueOf 函数从接口中获取目标对象信息 3. 反 ...
- GO语言基础之reflect反射
反射reflection 1. 反射可以大大的提高程序的灵活性,使得 interface{} 有更大的发挥余地 2. 反射使用 TypeOf 和 ValueOf 函数从接口中获取目标对象信息 3. 反 ...
- python的reflect反射方法
核心内容专自:http://www.liujiangblog.com/course/python/48 在自动化测试的时候,需要从excel中读取关键字,此关键字对应一个方法,如何使用该关键字去调用真 ...
- java reflect反射---Java高级开发必须懂的
理解反射对学习Java框架有很大的帮助,如Spring框架的核心就是使用Java反射实现的,而且对做一些Java底层的操作会很有帮助. 一.Class类的使用 1.万事万物皆对象,( ...
随机推荐
- [笔记]scanf的使用(主要是针对char)
学的是C++,用cin cout也用的很顺溜,写自己的类时重载"<<"与">>"运算符也很爽,但是发现在刷算法竞赛题时,cin cout ...
- Win10更新补丁失败后出现无法更新正在撤销 解决办法
系统更新失败,反复重启还是不行,那是不是下载下来的补丁没用了呢??所以我们先要删除Windows更新的缓存文件!在做以下操作之前,首先我们要确认系统内的windows update & BIT ...
- 栈的Java简单实现
关于栈 栈(Stack)是限定只能在一段进行插入和删除操作的线性表. 进行插入和删除操作的一端称为“栈顶”(top),另一端称为“栈底”(bottom). 栈的插入操作称为“入栈”(push),栈的删 ...
- 【转】解决response.AddHeader("Content-Disposition", "attachment; fileName=" + fileName) 中文显示乱码
如果fileName为中文则乱码.解决办法是 方法1: response.setHeader("Content-Disposition", "attachment; fi ...
- python3.5 安装lxml
通过xpath 爬虫时,使用到了lxml,通过pip 安装lxml 报错"building 'lxml.etree' extension building 'lxml.etree' ext ...
- DB太大?一键帮你收缩所有DB文件大小(Shrink Files for All Databases in SQL Server)
本文介绍一个简单的SQL脚本,实现收缩整个Microsoft SQL Server实例所有非系统DB文件大小的功能. 作为一个与SQL天天打交道的程序猿,经常会遇到DB文件太大,把空间占满的情况: 而 ...
- 多线程编程-- part 3 多线程同步->synchronized关键字
多线程同时访问一个资源,可以会产生不可预料的结果,所以为这个资源加锁,访问资源的第一个线程为其加锁后,其他线程便不能在使用那个资源,直到锁被解除. 举个例子: 存款1000元,能取出800的时候我就取 ...
- 2017-5-29 Excel VBA 小游戏
---恢复内容开始--- 转一个Excel VBA的小游戏,最近对excel有了更深入的了解,功能很强大,也刷新了我对待事情的态度. 一.准备界面 我们先来把游戏界面准备好,选中前4行,行高调成50, ...
- apache和nginx支持SSI配置
今天发现了一种新的语言格式:.shtml 一. 前言 SSI是一种类似于ASP的基于服务器的网页制作技术.将内容发送到浏览器之前,可以使用"服务器端包含 (SSI)"指令将文本.图 ...
- 源码分析 Large-Margin Softmax Loss for Convolutional Neural Networks
作者在Caffe中引入了一个新层,一般情况在Caffe中引入一个新层需要修改caffe.proto,添加该层头文件*.hpp,CPU实现*.cpp,GPU实现*.cu,代码结果如下图所示: caffe ...