反射帮助类
这个帮助类主要是用反射技术来实现的,既然说到反射那我们就来说说反射吧!
1、概念:这是.Net中获取运行时类型信息的方式,.Net的应用程序由几个部分:'程序集(Assembly)’、'模块(Module)’、'类型(class)’组成,而反射提供一种编程的方式,让程序员可以在程序运行期获得这几个组成部分的相关信息,例如:
通常程序员面试题,有这样关于反射的解释:反射可以动态地创建类型的实例,还可以将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。
2、下面我们来说说反射中经常用到的几个类吧!
a) Assembly类可以获得正在运行的装配件信息,也可以动态的加载装配件,以及在装配件中查找类型信息,并创建该类型的实例。
i. 下面是关于Assembly类的例子
public void AssemblyTest()
{
Console.WriteLine("----------------Assembly 类的使用----------------");
Console.WriteLine();
//获取当前所执行代码的程序集信息
Assembly assembly=Assembly.GetExecutingAssembly();
Console.WriteLine("获取程序集的位置:"+assembly.CodeBase);
Console.WriteLine("获取程序集的入口点:"+assembly.EntryPoint);
Console.WriteLine("获取程序集的显示名称:"+assembly.FullName);
Console.WriteLine("获取包含当前程序集清单的模块:"+assembly.ManifestModule);
Console.WriteLine();
//获取当前程序集的名称和信息
AssemblyName assemblyName=assembly.GetName();
Console.WriteLine("获取或设置程序集的简单名称:"+assemblyName.Name);
Console.WriteLine("获取程序集的名称:"+assemblyName.FullName);
Console.WriteLine("获取或设置程序集的URL位置:"+assemblyName.CodeBase);
Console.WriteLine("获取或设置程序集的主版本号、次版本号、内部版本号和修订版本号: "+assemblyName.Version);
Console.WriteLine();
//获取当前程序集的版本相关信息
Version version=assemblyName.Version;
Console.WriteLine("获取当前程序集的主版本号:"+version.Major);
Console.WriteLine("获取当前程序集的次版本号:" +version.Minor);
Console.WriteLine("获取当前程序集的内部版本号:" +version.Build);
Console.WriteLine("获取当前程序集的修订版本号:" +version.MajorRevision);
Console.WriteLine();
}
ii.
b) Type类可以获得对象的类型信息,此信息包含对象的所有要素:方法、构造器、属性等等,通过Type类可以得到这些要素的信息,并且调用之。
i. 下面是关于Type类的例子:
public void TypeTest()
{
Console.WriteLine("-----------------------Type类的使用-------------------------");
Console.WriteLine();
//设置被检索的类
Type myType=Type.GetType("Demo.PersonClass");
//Type myType = new PersonClass().GetType();
//检索信息
Console.WriteLine("获取当前成员名称:"+myType.Name);
Console.WriteLine("获取当前完全限定名(不包括程序信):" +myType.FullName);
Console.WriteLine("获取当前Type所在的够命名空间:"+myType.Namespace);
Console.WriteLine();
//检索类成员
Console.WriteLine("获取方法相关信息:"+myType.GetMethod("MetName").ToString());
Console.WriteLine("获取属性相关信息:"+myType.GetProperty("Name").ToString());
Console.WriteLine("获取字段相关信息:"+myType.GetField("name",BindingFlags.NonPublic| BindingFlags.Instance).ToString());
Console.WriteLine();
//设定自己为被检索的类
Type thisType=this.GetType();
}
c) 访问类成员中的常用到的几个类:
///<summary>
/// 访问类成员±
///</summary>
public void ClassMemberInfo()
{
//指定被访问的类
//Type myType = Type.GetType("Demo.PersonClass")
//或者这样指定被访问的类:这样可以获取属性值但要用到对象实例,
PersonClass person=newPersonClass();
Type personType=person.GetType();
Console.WriteLine("-----------MemberInfo访问类的所有成员----------");
Console.WriteLine();
//MemberInfo类:遍历访问类中所有成员
MemberInfo[] memberInfo=personType.GetMembers(BindingFlags.Public|BindingFlags.NonPublic
|BindingFlags.Static|BindingFlags.Instance|BindingFlags.DeclaredOnly);
foreach (MemberInfo minmemberInfo)
{
Console.WriteLine("成员名称:{0} ----------- 成员类型:{1}", m.Name, m.MemberType);
}
Console.WriteLine();
Console.WriteLine("--------------------------MethodInfo类访问类的方法-----------------------");
Console.WriteLine();
//MethodInfo类:遍历被访问类的方法
MethodInfo[] methodInfo=personType.GetMethods(BindingFlags.Public|BindingFlags.NonPublic| BindingFlags.Instance|BindingFlags.DeclaredOnly);
foreach (MethodInfo minmethodInfo)
{
Console.WriteLine("方法名称:{0} ----------- 方法类型:{1}, {2}", m.Name, m.ReturnType);
}
Console.WriteLine();
Console.WriteLine("------------------PropertyInfo类访问属性------------------");
Console.WriteLine();
//Propertyinfo类:遍历被访问类的属性
PropertyInfo[] prop=personType.GetProperties(BindingFlags.Public|BindingFlags.NonPublic| BindingFlags.Instance|BindingFlags.DeclaredOnly);
foreach (PropertyInfo pinprop)
{
Console.WriteLine("属性名称:{0} 属性类型:{1} 属性值:{2}", p.Name, p.PropertyType, p.GetValue(person, null));
}
Console.WriteLine();
Console.WriteLine("---------------------------FieldInfo类访问字段------------------------------");
Console.WriteLine();
//FieldInfo类:遍历被访问类的字段
FieldInfo[] fieldInfo=personType.GetFields(BindingFlags.Public|BindingFlags.NonPublic| BindingFlags.Instance|BindingFlags.DeclaredOnly);
foreach (FieldInfo finfieldInfo)
{
Console.WriteLine("字段名称:{0} --------- 字段类型:{1}", f.Name, f.FieldType);
}
Console.WriteLine();
Console.WriteLine("---------------ConstructorInfo类访问构造函数------------");
Console.WriteLine();
//ConstructorInfo类:遍历被访问类的构造函数
ConstructorInfo[] constructor=personType.GetConstructors(BindingFlags.Public|BindingFlags.NonPublic| BindingFlags.Instance|BindingFlags.DeclaredOnly);
foreach (ConstructorInfo cinconstructor)
{
Console.WriteLine("构造函数名称:{0} ------- 构造函数类型:{1}", c.Name, c.MemberType);
}
Console.WriteLine();
Console.WriteLine("----------------EventInfo类访问类的事件元数据---------------");
Console.WriteLine();
//EventInfo类:遍历被访问类的事件
EventInfo[] eventInfo=personType.GetEvents(BindingFlags.Public|BindingFlags.NonPublic| BindingFlags.Instance|BindingFlags.DeclaredOnly);
foreach (EventInfo eineventInfo)
{
Console.WriteLine("事件名称:{0} -------- 成员类型:{1}", e.Name, e.MemberType);
}
Console.WriteLine();
Console.WriteLine("--------------ParameterInfo用于表示参数元数据-----------");
Console.WriteLine();
//ParameterInfo类:检索PersonClass类中MetName()方法中的参数
MethodInfo method=personType.GetMethod("TestName"); //("TestName")为方法名
ParameterInfo[] para=method.GetParameters();
int count=0;
foreach (ParameterInfo pinpara)
{
count++;
Console.WriteLine("参数" +count+"名称:{0}, 类型:{1}", p.Name, p.ParameterType);
}
Console.WriteLine();
}
d) 调用类成员
///<summary>
/// 调用类成员
/// 第一种调用方法就是用MethodInfo类调用方法,PropertyInfo类调用属性,FieldInfo类调用字段,EventInfo类调用事件, /// ConstructorInfo类调用构造函数
/// 第二种方法就是用Type类的InvokeMember()方法
///</summary>
public void OperMembers()
{
//指定被访问的类
PersonClass personClass=newPersonClass();
Type type=personClass.GetType();
// 或者这样指定被访问类(注意:静态类时(“GetType(“命名空间.类名”)”))
//Type type = Type.GetType("Demo.PersonClass");
//操作方法
string[] strName=newString[] { "我是谁" }; //方法参数的值
MethodInfo methodInfo=type.GetMethod("MetName"); //("MetName")是方法名
methodInfo.Invoke(personClass, strName);
Console.WriteLine("操作方法改变值后为:" +personClass.Name);
//操作属性
PropertyInfo property=type.GetProperty("Name"); //("Name")表示属性名
property.SetValue(personClass, "wxy", null); //"wxy"为新值 SetValue为设置属性的值,GetValue为获取属性的值
Console.WriteLine("操作属性改变后的值为:"+personClass.Name);
//操作字段
FieldInfo field=type.GetField("name", BindingFlags.NonPublic|BindingFlags.Instance);
field.SetValue(personClass, "hehe"); //"hehe" 表示给字段赋的新值
Console.WriteLine("操作字段改变后的值为:"+field.Name );
Console.WriteLine();
//操作事件
EventInfo eventInfo=type.GetEvent("WMShout"); //("WMShout")表示的是事件名
Console.WriteLine("声明该成员的类:"+eventInfo.DeclaringType);
Console.WriteLine("事件名称:"+eventInfo.Name);
Console.WriteLine("事件类型:"+eventInfo.MemberType);
Console.WriteLine();
//操作构造函数
ConstructorInfo constructor=type.GetConstructor(System.Type.EmptyTypes);
}
下面是我封装的一个Helper类,这个类主要用于一个对象给另一个对象赋值(就是一个对象中的属性或字段想要得到与另一个对象相同的值那么就可以可以用这封装的这个类):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Collections;
using System.IO;
namespace ReflectionDemo
{
public class ReflectionHelper
{
///<summary>
///将一个对象中的属性值赋给对应的另一个对象中的属性
///</summary>
///<param name="target">被赋值的对象</param>
///<param name="initial">赋值的对象</param>
public static void GetObjectAttributeAssignmentAnotherObject(objecttarget, objectinitial)
{
//获取被赋值对象的属性
PropertyInfo[] propertyInfo1=target.GetType().GetProperties(BindingFlags.Public|
BindingFlags.NonPublic|BindingFlags.Instance|BindingFlags.DeclaredOnly);
//获取赋值对象的属性
PropertyInfo[] propertyInfo2=initial.GetType().GetProperties(BindingFlags.Public|
BindingFlags.NonPublic|BindingFlags.Instance|BindingFlags.DeclaredOnly);
foreach (PropertyInfo propertyinpropertyInfo1) //遍历被赋值对象的属性
{
foreach (PropertyInfo pinpropertyInfo2) //遍历赋值对象的属性
{
//判断被赋值对象与赋值对象中的属性名和属性类型都同那就赋值
if (property.Name==p.Name&&property.PropertyType==p.PropertyType&&property.CanWrite==true)
{
property.SetValue(target, p.GetValue(initial, null), null); //向对象设置值(SetValue为置,GetValue为获取)
}
}
}
}
///<summary>
///将一个对象中的属性值赋给对应的另一个对象中的属性
///</summary>
///<param name="target">被赋值的对象</param>
///<param name="initial">赋值的对象</param>
///<param name="ht">保存两个对象中不一样的属性(被赋值的保存成Key,赋值的保存成Value)</param>
public static void SetObjectAttributes(objecttarget, objectinitial,Hashtableht)
{
//获取被赋值对象的属性
PropertyInfo[] propertyInfo1=target.GetType().GetProperties(BindingFlags.Public|
BindingFlags.NonPublic|BindingFlags.Instance|BindingFlags.DeclaredOnly);
//获取赋值对象的属性
PropertyInfo[] propertyInfo2=initial.GetType().GetProperties(BindingFlags.Public|
BindingFlags.NonPublic|BindingFlags.Instance|BindingFlags.DeclaredOnly);
foreach (PropertyInfo propertyinpropertyInfo1) //遍历被赋值对象的属性
{
foreach (PropertyInfo pinpropertyInfo2) //遍历赋值对象的属性
{
if (ht.Count>0) //保证Hashtable中有数据
{
if (ht[property.Name] !=null) //判断Hashtable中的key值在Hashtable中是否能找到
{
if (ht[property.Name].Equals(p.Name)) //判断两个对象中和的属性在Hashtable中是否是相对应
{
property.SetValue(target, p.GetValue(initial, null), null);//赋值(SetValue为设置值,GetValue取值)
continue;
}
}
}
//判断被赋值对象与赋值对象中的属性名和属性类型都相同那就赋值
if (property.Name==p.Name&&property.PropertyType==p.PropertyType&&property.CanWrite==true)
{
property.SetValue(target, p.GetValue(initial, null), null);
}
}
}
}
///<summary>
///将一个对象中的字段值赋给对应的另一个对象中的字段
///</summary>
///<param name="obj1">需要被赋值的对象</param>
///<param name="obj2">已经有值的对象</param>
public static void SetObjectField(objecttarget, objectinitial)
{
//获取被赋值对象中的字段
FieldInfo[] fieldInfo1=target.GetType().GetFields(BindingFlags.Public|
BindingFlags.NonPublic|BindingFlags.Instance|BindingFlags.DeclaredOnly);
//获取赋值对象中的字段
FieldInfo[] fieldInfo2=initial.GetType().GetFields(BindingFlags.Public|
BindingFlags.NonPublic|BindingFlags.Instance|BindingFlags.DeclaredOnly);
foreach (FieldInfo fieldinfieldInfo1)
{
foreach (FieldInfo finfieldInfo2)
{
//判断被赋值对象与赋值对象的名称与类型是否都相同
if (field.Name==f.Name&&field.FieldType==f.FieldType)
{
field.SetValue(target, f.GetValue(initial)); //赋值(SetValue为设置值,GetValue为获取值)
}
}
}
}
///<summary>
///将一个对象中的属性值赋给对应的另一个对象中的属性(包含不一样的属性名称,但要一一对应)
///</summary>
///<param name="target">被赋值对象</param>
///<param name="initial">赋值对象</param>
///<param name="dic">这个集合存放两个对象中不一样属性名称的,被赋值对象属性被存放为Key,赋值对象被存放为Value</param>
public static void GetObjectAttributeAssignmentAnotherObject(objecttarget, objectinitial, IDictionary<object, object>dic)
{
//获取被赋值对象的属性
PropertyInfo[] propertyInfoObj1=target.GetType().GetProperties(BindingFlags.Public|
BindingFlags.NonPublic|BindingFlags.Instance|BindingFlags.DeclaredOnly);
//获取赋值对象的属性
PropertyInfo[] propertyInfoObj2=initial.GetType().GetProperties(BindingFlags.Public|
BindingFlags.NonPublic|BindingFlags.Instance|BindingFlags.DeclaredOnly);
foreach (PropertyInfo propertyinpropertyInfoObj1) //遍历被赋值对象的属性
{
foreach (PropertyInfo pinpropertyInfoObj2)
{
if (dic.Count>0)
{
//判断dic对象中Key值是否存在,如果不存在但以用了的话那就会出KeyNotFoundException异常
if (dic.Keys.Contains(property.Name) &&dic[property.Name].Equals(p.Name))
{
property.SetValue(target, p.GetValue(initial, null), null);
continue;
}
}
if (property.Name==p.Name&&property.PropertyType==p.PropertyType&&property.CanWrite==true)
{
property.SetValue(target, p.GetValue(initial, null), null);
}
}
}
}
}
}
反射在下列情况下很有用:
需要访问程序元数据的属性。请参见主题使用反射访问属性。
检查和实例化程序集中的类型。
在运行时构建新类型。使用 System.Reflection.Emit 中的类。
执行后期绑定,访问在运行时创建的类型的方法。请参见主题动态加载和使用类型。
反射的一般概念:
反射提供了封装程序集、模块和类型的对象。您可以使用反射动态地创建类型的实例,将类型绑定到现有对 象,或从现 有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。我觉得这个已经是对反射比较干脆利落 的诠 释。这里的”动态”二字非常重要,即指的是运行时刻,在运行的时刻我们可以根据程序逻辑或外部要求(比如配置 文件等)创建类型的实例,然后实现象用new构造对象一样同等的效果。当然构造了这个实例之后下来的访问和以前就一 样。
反射的效率:
采用反射构造对象的实例,机器明显出现延迟,不言而喻,反射是以牺牲效率为代价的。
优点:
1.反射是一个非常优秀的代码生成替代工具,能够显著减少代码长度,并缓解应用维护压力。
2.可以降低不同模块之间的依赖关系,大大提高可维护性。
缺点:
反射是以牺牲效率为代价的即消耗性能大。
下面是测试反射性能的代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace WindowsFormsApplication1
{
public class CTester
{
private double a;
public CTester()
{
a=10;
}
public void test1()
{
a= (a-0.0001) *1.0001;
}
public double Geta()
{
return a;
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(objectsender, EventArgse)
{
}
private void button1_Click(objectsender, EventArgse)
{
int dt1=DateTime.Now.Minute*60000+DateTime.Now.Second*1000+DateTime.Now.Millisecond;
CTester aTest=newCTester();
for (int i=0; i<1000; i++)
{
for (int j=0; j<100; j++)
{
aTest.test1();
}
}
int dt2=DateTime.Now.Minute*60000+DateTime.Now.Second*1000+DateTime.Now.Millisecond;
int spand=dt2-dt1;
label1.Text="用掉时间:" +spand.ToString();
label3.Text="值为:" +aTest.Geta();
//test1();
}
private void button2_Click(objectsender, EventArgse)
{
int dt1=DateTime.Now.Minute*60000+DateTime.Now.Second*1000+DateTime.Now.Millisecond;
//如果将下面两个句话放入循环中,时间会用的更久
Type theTest=Type.GetType("WindowsFormsApplication1.CTester");
object theobj=theTest.InvokeMember(null, BindingFlags.CreateInstance, null, null, null);
for (int i=0; i<1000; i++)
{
for (int j=0; j<100; j++)
{
theTest.InvokeMember("test1", BindingFlags.InvokeMethod, null, theobj, newobject[0]);
}
}
CTester thewar=theobjasCTester;
int dt2=DateTime.Now.Minute*60000+DateTime.Now.Second*1000+DateTime.Now.Millisecond;
int spand=dt2-dt1;
label2.Text="用掉时间:" +spand.ToString();
label4.Text="值为:" +thewar.Geta();
//test2();
}
//首先我们对于对象的构造进行测试
//测试代码如下
private void test1()
{
label1.Text="";
label3.Text="";
int dt1=DateTime.Now.Minute*60000+DateTime.Now.Second*1000+DateTime.Now.Millisecond;
for (int i=0; i<1000; i++)
{
for (int j=0; j<100; j++)
{
CTester aTest=newCTester();
}
}
int dt2=DateTime.Now.Minute*60000+DateTime.Now.Second*1000+DateTime.Now.Millisecond;
int spand=dt2-dt1;
label1.Text="用掉时间:" +spand.ToString();
}
private void test2()
{
label2.Text="";
label4.Text="";
int dt1=DateTime.Now.Minute*60000+DateTime.Now.Second*1000+DateTime.Now.Millisecond;
for (int i=0; i<1000; i++)
{
for (int j=0; j<100; j++)
{
Type theTest=Type.GetType("WindowsFormsApplication1.CTester");
object theobj=theTest.InvokeMember(null, BindingFlags.CreateInstance, null, null, null);
}
}
int dt2=DateTime.Now.Minute*60000+DateTime.Now.Second*1000+DateTime.Now.Millisecond;
int spand=dt2-dt1;
label2.Text="用掉时间:" +spand.ToString();
}
}
}
//虽然只用invokemember尝试了一些简单的反射,但是很显然的,反射得消耗是非常大