Load方法:CLR通过调用System.Rreflection.Assemblly类的静态方法来显示加载程序集。
public static Assembly Load(AssemblyName assemblyRef); public static Assembly Load(string assemblyString);
LoadFrom方法:此方法首先打开程序集,并通过 public static AssemblyName GetAssemblyName(string assemblyFile);方法提取到程序集名称,然后再使用Load方法加载程序集。
public static Assembly LoadFrom(string path);
ReflectionOnlyLoad方法:加载程序集,只是获取程序集的相关信息。但CLR禁止此程序集中的代码执行
public static Assembly ReflectionOnlyLoad(string assemblyString);
ReflectionOnlyLoadFrom
public static Assembly ReflectionOnlyLoadFrom(string path);
System.Type.GetType方法 :字符串必须执行的是全名,对于基元类型不能识别,识别CLR类型。
public static Type GetType(string typeName);
System.Type.ReflectionOnlyGetType:只是显示反射上下文内容,不能执行代码。
public static Type ReflectionOnlyGetType(string typeName, bool throwIfNotFound, bool ignoreCase);
typeof()
static Encryption(object o) { //早期绑定:o.GetType() //晚期绑定typeof if (o.GetType() == typeof(Encryption)) { } }
创造类型实例
System.Activator.CreateInstance
public static object CreateInstance(Type type); public static ObjectHandle CreateInstance(string assemblyName, string typeName);
上面的方法返回值有的是ObjectHandle类型,此类型允许将一个AppDomian中的对象传送到另一个AppDomian而且不需要具体化哪个对象。如果需要具体化具体的类型就可以调用 Unwrap()方法。
System.Activator.CreateComInstanceFrom:程序集和类型都是字符串直接指定,不过,程序集需要使用LoadFrom方法来加载得到程序集,并且提取名称作为参数传递。当然对于返回值我们也需要调用Unwrap()进行具体化。
public static ObjectHandle CreateComInstanceFrom(string assemblyName, string typeName);
System.Reflection.Assembly assy= System.Reflection.Assembly.LoadFrom(""); System.Activator.CreateComInstanceFrom(assy.FullName, "Encryption");
MemberInfo的成员组成
DeclaringType:声明方法的类
ReflectedType:当前的反射类
public class UnitTest1 { [TestMethod] public void TestMethod1() { System.Reflection.MemberInfo[] mi = typeof(MyType).GetMembers(); foreach (var m in mi) { var dt= m.DeclaringType;//ToSting() var rt = m.ReflectedType;//Equal() } } } public sealed class MyType { public override string ToString() { return null; } }
BindingFlags:筛选返回的成员种类
我们可以调用Type的 GetMethod、GetProperty、GetNestedType各种Get。
我们可以组合BindingFlags,来筛选我们需要的东西。
我们可以找出我们需要的东东,然后我们可以进行调用。调用属性、方法、构造函数等等。
调用FieldInfo,可以获取或者设置字段的值;
调用ConstructorInfo,可以向构造传递实参,从而构造类型的一个实例;
调用MethodInfo,可以通过传递实参来调用方法,并返回它的返回值;
调用PropertyInfo,可以调用属性的get和set访问其方法。
调用方法
public object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, CultureInfo culture);
调用此方法可以在成员类型里搜索一个匹配的类型成员,如果没找到,抛出异常。如果找到,就会调用此成员。我们可以归纳以上行为为两个阶段:绑定和调用。
我们调用一个绑定器方法时,首先我们传递 目标成员 name、方法的所有参数类型 args 、指定的 BindingFlags invokeAttr 。
参数 name :目标成员。名字
参数 invokeAttr :这些位标识的组合可以帮助我们更好的定位我们的成员 IgnoreCase = 1, DeclaredOnly = 2,Instance = 4,Static = 8,Public = 16等
参数 binder :它的类型从System.Relflection.Binder 抽象类型派生的,从Binder派生的类型,封装了InverMember方法确定绑定类型的规则。编译器为我们定义了System.DefaultBinder 的实际类型可以为我们使用。可以帮助我们自动的类型转换。
参数 target : 目标成员所在类的实例的一个引用。静态类 =null。
参数 args: 传递的参数。
参数 culture: 如果有自己的绑定器可以使用此参数,如果前面参数使用了System.DefaultBinder,culture参数就可以省略了。
InvokeMember方法可以,访问任何成员。但是如果多次调用,我们就需要多次进行绑定并调用,比较损耗性能。我们可以使用
实践
环境 vs2015
新建控制台 DLLLoadDemo
新建IServices类库,放接口
新建JDPayServices类库放实现
2016-2-5 ===== AYUI www.ayjs.net AY ======
DLLLoadDemo和IServices项目生成目录都改到上级目录的bin下
JDPayServices放到bin下的PayServices文件夹下
IService下新建IPayService
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace IServices { public interface IPayService { string PayName { get; set; } string UserId { get; set; } string BusinessId { get; set; } decimal Money { get; set; } DateTime OrderDate { get; set; } string NotifyUrl { get; set; } string CompleteUrl { get; set; } void Pay(); void Pay(string remark); bool Notify(); } }
在JDPayService新建统一名字的PayService,引用IServices项目,然后继承IPayService,并实现
using IServices; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace JDPayService { public class PayService : IPayService { public PayService() { } public PayService(string payname,string userid,string businessid,decimal money,DateTime orderDate,string notifyUrl,string completeUrl) { this.PayName = payname; this.UserId = userid; this.BusinessId = businessid; this.Money = money; this.OrderDate = orderDate; this.NotifyUrl = notifyUrl; this.CompleteUrl = completeUrl; } public string PayName { get; set; } public string UserId { get; set; } public string BusinessId { get; set; } public decimal Money { get; set; } public DateTime OrderDate { get; set; } public string NotifyUrl { get; set; } public string CompleteUrl { get; set; } public void Pay() { Console.WriteLine("============="+ PayName +"============="); Console.WriteLine(string.Format("{0}:{1}向{2}支付了{3},成功!", OrderDate.ToString("F"), UserId, BusinessId, Money.ToString("C"))); Console.WriteLine("=============交易结束============="); } public void Pay(string remark) { Console.WriteLine("=============" + PayName + "============="); Console.WriteLine(string.Format("{0}:{1}向{2}支付了{3},成功!",OrderDate.ToString("F"),UserId,BusinessId,Money.ToString("C"))); Console.WriteLine("备注:"+remark); Console.WriteLine("=============交易结束============="); } public bool Notify() { Console.WriteLine("已经通知:"+NotifyUrl); return true; } } }
调整引用的IServices.dll的属性,不要复制到本地
DLLLoadDemo引用IServices,重新生成解决方案
准备工作完成了,接下来,在DLLLoadDemo代码里面写 反射代码,学习。
获取当前主程序所在路径
string hostFilePath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location)+ "/PayServices";
然后我们获得所有的支付方式dll
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DLLLoadDemo { class Program { static void Main(string[] args) { //获取当前主程序所在路径 string hostFilePath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location)+ "/PayServices"; //获取dll路径 string[] dlls = System.IO.Directory.GetFiles(hostFilePath, "*.dll"); foreach (string dll in dlls) { Console.WriteLine(dll); } Console.ReadLine(); } } }
运行效果:
获得程序集名字 Name和FullName
一个程序集多个类和其他的成员组成的
//获取程序集中所有公共类 Type[] types = assembly.GetExportedTypes(); foreach (var item in types) { Console.WriteLine("Type name :{0}", item.FullName); }
断点查看,还有很多其他属性,你可以过滤
判断是不是继承IPayService的类,是的话,获得所有成员
foreach (var item in types) { Console.WriteLine("Type name :{0}", item.FullName); if (item.IsClass && typeof(IServices.IPayService).IsAssignableFrom(item)) { //获取当前类中公共成员 System.Reflection.MemberInfo[] members = item.GetMembers(); } }
构造实例
//获取程序集中所有公共类 Type[] types = assembly.GetExportedTypes(); foreach (var item in types) { Console.WriteLine("Type name :{0}", item.FullName); if (item.IsClass && typeof(IServices.IPayService).IsAssignableFrom(item)) { //获取当前类中公共成员 System.Reflection.MemberInfo[] members = item.GetMembers(); IServices.IPayService pay = (IServices.IPayService)System.Activator.CreateInstance(item,"京东支付","ayBuy","ayBus",100.00M,DateTime.Now,"http://www.ayjs.net/rest/no1", "http://www.ayjs.net/rest/nocomplete"); if (pay != null) { pay.Pay("ay购买BB鸡,875556003@qq.com"); } } }
调用dll中符合接口的约定的 方法
获得属性:
Console.WriteLine("----属性----"); System.Reflection.PropertyInfo[] ppinfo = item.GetProperties(); foreach (var p in ppinfo) { Console.WriteLine("属性: {0} (get:{1},set:{2})", p.Name, p.CanRead.ToString(), p.CanWrite.ToString()); Console.WriteLine("{0}={1}", p.Name,p.GetValue(pay,null)); }
获得字段:
我们先在PayService加上几个测试字段
private const string version = "1.2.5.7"; public const string author = "ay"; public readonly static int age = 25; public bool isRead = false; public string wuString; public string nullString = null;
Console.WriteLine("----字段----"); System.Reflection.FieldInfo[] fields = item.GetFields(); foreach (var f in fields) { object v = f.GetValue(pay); Console.WriteLine("普通字段 {0} {1} {2}", f.Name, f.FieldType.ToString(),v==null?"空":v.ToString()); }
效果图:
只读取了 非private的字段。
稍微修改
System.Reflection.FieldInfo[] fields = item.GetFields(); foreach (var f in fields) { object v = f.GetValue(pay); //Console.WriteLine("普通字段 {0} {1} {2}", f.Name, f.FieldType.ToString(),v==null?"空":v.ToString()); Console.WriteLine("public {0}{1} {2} {3}",f.IsStatic?"static ":"", f.FieldType.ToString(), f.Name, v == null ? " 空 " : "="+v.ToString()); }
2016-2-5 ======== AYUI www.ayjs.net AY ===============
方法
//方法 Console.WriteLine("----方法----"); System.Reflection.MethodInfo[] methodsinfo = item.GetMethods(); foreach (var m in methodsinfo) { //返回后打印出类中成员的类型(方法的返回值类型)极其方法名称 Console.WriteLine(" " + m.ReturnType.Name + " " + m.Name + "");//ReturnType获取此方法的返回类型 }
发现了2个pay,这是个重载的方法,所以调用,我们要知道参数,决定调用哪个
获得方法的参数,显示
//方法 Console.WriteLine("----方法----"); System.Reflection.MethodInfo[] methodsinfo = item.GetMethods(); foreach (var m in methodsinfo) { //返回后打印出MyClass类中成员的类型(方法的返回值类型)极其方法名称 Console.Write(" " + m.ReturnType.Name + " " + m.Name + "(");//ReturnType获取此方法的返回类型 ParameterInfo[] pi = m.GetParameters();//获得方法的参数 for (int i = 0; i < pi.Length; i++) { Console.Write(pi[i].ParameterType.Name + " " + pi[i].Name);//ParameterType 获取该参数的Type(类型) if (i + 1 < pi.Length) { Console.Write(", "); } } Console.Write(")"); Console.WriteLine(); }
ay的效果图:
这里它把属性的方法也放出来了,父类的方法也是
ay的方法筛选 BindingFlags
System.Reflection.MethodInfo[] methodsinfo = item.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public); foreach (var m in methodsinfo) { //返回后打印出MyClass类中成员的类型(方法的返回值类型)极其方法名称 Console.Write(" " + m.ReturnType.Name + " " + m.Name + "(");//ReturnType获取此方法的返回类型 ParameterInfo[] pi = m.GetParameters();//获得方法的参数 for (int i = 0; i < pi.Length; i++) { Console.Write(pi[i].ParameterType.Name + " " + pi[i].Name);//ParameterType 获取该参数的Type(类型) if (i + 1 < pi.Length) { Console.Write(", "); } } Console.Write(")"); Console.WriteLine(); }
调用方法
代码:
System.Reflection.MethodInfo[] methodsinfo = item.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public); foreach (var m in methodsinfo) { //demo2 www.ayjs.net if (m.Name == "Pay") { ParameterInfo[] pi = m.GetParameters();//获得方法的参数 if (pi.Count() == 1) { object[] objs = new object[] { "ay买了个iphone7是个,是个模型" }; m.Invoke(pay, objs); } else if (pi.Count() == 0) { m.Invoke(pay, null); } Console.WriteLine(); } }
使用类型的InvokeMember方法,重载也不需要考虑了,自动识别,但是调用多了,性能肯定不好。
//demo3 object[] para = new object[] { "ay买了个诺基亚,砸了一天的夏威夷果" }; item.InvokeMember("Pay", System.Reflection.BindingFlags.InvokeMethod, null, pay, para);
属性赋值:
//属性赋值 //获取指定名称的属性 System.Reflection.PropertyInfo propertyInfo = item.GetProperty("Money"); propertyInfo.SetValue(pay, 520.00M, null); //给对应属性赋值 decimal value_New = (decimal)propertyInfo.GetValue(pay, null); Console.WriteLine("Money新值:"+value_New.ToString("C"));
=============好了,先到这里,这是反射基础,后面还有Emit,泛型,dynamic,表达式树,委托的反射创建等 等
下面是我看到的一个DataTable里的数据转到List<T>的数据的DEMO
/// <summary> /// 将datatable装入指定类型的集合 /// </summary> /// <typeparam name="T"></typeparam> public class GenericList<T>:List<T> { public GenericList(DataTable dt, string f) { System.Type tt = System.Type.GetType(f);//获取指定名称的类型 object ff = Activator.CreateInstance(tt, null);//创建指定类型实例 PropertyInfo[] fields = ff.GetType().GetProperties();//获取指定对象的所有公共属性 foreach (DataRow dr in dt.Rows) { object obj = Activator.CreateInstance(tt, null); foreach (DataColumn dc in dt.Columns) { foreach (PropertyInfo t in fields) { if (dc.ColumnName == t.Name) { t.SetValue(obj, dr[dc.ColumnName], null);//给对象赋值 continue; } } } this.Add((T)obj);//将对象填充到list集合 } } } }
这里的f是个类型字符串
GenericList<Employee> list1 = new GenericList<Employee>(dt, "MvcApplication2.Models.Employee");
但是这里我建议不传字符串,而是传个type类型过去