C#反射机制
反射是.NET中的重要机制。
通过反射,可以在运行时获得程序或程序集中每一个类型(包括类、结构、委托、接口、和枚举等)的成员和成员信息。
1)使用Assembly定义和加载程序集,加载在程序集清单中列出模块,以及从此程序集中查找类型并创建该类型的实例。
2)使用Module了解包含模块的程序集以及模块中的类等,还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。
3)使用ConstructorInfo了解构造函数的名称、参数、访问修饰符(如public 或private)和实现详细信息(如absract或virtual)等。
4)使用MethodInfo了解方法的名称、返回类型、参数、访问修饰符(如public或private)和实现详细信息(如abstract或virtual)等。
5)使用FiedInfo了解字段的名称、访问修饰符(public或private)和实现详细信息(如static)等,并获取或设置字段值
6)使用EventInfo了解事件的名称、时间处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序。
7)使用PropertyInfo了解属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,获取或设置属性值。
8)使用ParamterInfo了解参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中的位置等
反射用到的命名空间:
System.Reflection
System.Type
System.Reflection.Assembly
反射主要用到的类:
System.Type 类--通过这个类可以访问任何给定数据类型的信息。
System.Reflection.Assembly 类--它可以用于访问给定程序集的信息,或者把这个程序集加载到程序中。
System.Type 类对于反射起着核心的作用,它是一个抽象的基类,Type有与每种数据类型对应的派生类,我们使用这个派生类的对象的方法、字段、属性来查早有关该类型的所用信息
Type类的属性:
Name 数据类型名
FullName 数据类型的完全限定名(包含命名空间名)
Namespace 定义数据类型的命名空间名
IsAbstract 指示该类型是否是抽象类型
IsArray 指示该类型是否是数组
IsClass 指示该类型是否是类
IsEnum 指示该类型是否是枚举
IsInterface 指示该类型是否是接口
IsPublic 指示该类型是否是公有的
IsSealed 指示该类型是否是密封类
IsValueType 指示该类型是否是值类型
Type类的方法:
GetConstructor(),GetConstrutors():返回ConstructorInfo类型,用于取得该类型的构造函数的信息
GetEvent(),GetEvents():返回EventInfo类型,用于取得该类的事件的信息
GetField(),GetFields():返回FieldInfo类型,用于取得该类的字段(成员变量)的信息
GetInterface(),GetInterfaces():返回InterfaceInfo类型,用于取得该类实现的接口信息
GetMember(),GetMembers():返回MemberInfor类型,用于取得该类型的所有成员的信息
GetMethod(),GetMethods():返回MethodInfo类型,用于取得该类的方法的信息
GetProperty(),Getpropertys():返回PropertyInfo类型,用于取得该类的属性的信息
可以调用这些成员,其方式是调用Type的InvokMember()方法,或者调用MethodInfo,ProperInfo和其他类的Invoke()方法。
using System;
using System.Reflection;
namespace CustomNameSpace{
public class Person{
public string Name { set; get; }
public void Print(){
Console.WriteLine("Call Class CustomNameSpace.Person Print()");
}
}
}
namespace Refelection
{
public class NewClass
{
/*Field*/
public int iNum = 10;
public float fNum = 3.14f;
/*Property*/
public string Name { set; get; }
public int Age { set; get; }
/*Constructor*/
public NewClass(){
Console.WriteLine("NewClass() NewClass() NewClass()");
}
public NewClass(int num, string _name){
Console.WriteLine("NewClass(int num, string _name) " +
"NewClass(int num, string _name)");
}
/*Method*/
public void Exam(int num1, float num2){
Console.WriteLine(" mul = " + (num1 * num2));
}
public void Print(){
Console.WriteLine("I'm NewClass");
}
}
class MainClass
{
public void show(){
Console.WriteLine("Hello World");
}
public static void Main(string[] args)
{
//访问构造函数
//NewClass nc = new NewClass();
//Type t = nc.GetType();
//Type t = typeof(NewClass);
//ConstructorInfo[] constructorInfos =
// t.GetConstructors();
//Type [] param_array = new Type[2];
//if(constructorInfos.Length > 0){
// foreach(ConstructorInfo item in constructorInfos){
// ParameterInfo[] parameterInfos = item.GetParameters();
// if(parameterInfos.Length > 0){
// //foreach(ParameterInfo param in parameterInfos){
// // Console.WriteLine("typeof(param) = " +
// // param.ParameterType.FullName);
// //}
// for (int i = 0; i < parameterInfos.Length; i ++){
// param_array[i] = parameterInfos[i].ParameterType;
// }
// }
// }
//}
//构造函数动态生成对象
/*
ConstructorInfo ci = t.GetConstructor(param_array);
object[] parameters = new object[] { 123, "Reflection" };
object newClass = ci.Invoke(parameters);
((NewClass)newClass).Print();
*/
//Activator生成对象
//1
//object newInstace = Activator.CreateInstance(t);
//((NewClass)newInstace).Print();
//2
//object[] acti_param_array = new object[] {12321, "nihao" };
//object newInstance = Activator.CreateInstance(t, acti_param_array);
//((NewClass)newInstance).Print();
//3
//object newInstance = Activator.CreateInstance(t, 123, "asfdsa");
//((NewClass)newInstance).Print();
//查看类中的public字段
//FieldInfo[] fieldInfos = t.GetFields();
//foreach(FieldInfo field in fieldInfos){
// Console.WriteLine( field.Name );
//}
//查看类中的public属性
//PropertyInfo[] propertyInfos = t.GetProperties();
//foreach(PropertyInfo property in propertyInfos){
// Console.WriteLine( property.Name );
//}
//查看类中的public方法
//MethodInfo[] methodInfos = t.GetMethods();
//foreach(MethodInfo method in methodInfos){
// Console.WriteLine(method.ReturnType.FullName
// + " " + method.Name );
//}
//object new_Instance = Activator.CreateInstance(t);
//FieldInfo fi_1 = t.GetField("iNum");
//fi_1.SetValue(new_Instance, 10);
//FieldInfo fi_2 = t.GetField("fNum");
//fi_2.SetValue(new_Instance, 10.1f);
////Console.WriteLine(fi_1.GetValue(new_Instance));
//PropertyInfo pi_1 = t.GetProperty("Name");
//pi_1.SetValue(new_Instance, "zhangsan");
//PropertyInfo pi_2 = t.GetProperty("Age");
//pi_2.SetValue(new_Instance, 123);
// Console.WriteLine (pi_2.GetValue(new_Instance));
//MethodInfo mi = t.GetMethod("Exam");
// mi.Invoke (new_Instance, new object[]{fi_1.GetValue(new_Instance),
//fi_2.GetValue(new_Instance)});
//System.Reflection.Assembly介绍
//1、通过程序集名称返回Assembly对象
/*
Assembly assembly = Assembly.Load("Refelection");
Console.WriteLine(assembly.FullName);
Object new_person_instance =
assembly.CreateInstance("CustomNameSpace.Person");
((CustomNameSpace.Person)new_person_instance).Print();
*/
//2、通过DLL文件名称返回Assembly对象
//Assembly assembly = Assembly.LoadFrom(@"/Users/ww/Desktop/MyDLL.dll");
//Type type = assembly.GetType("MyDLL.MyClass");
//object new_myclass_instance = Activator.CreateInstance(type);
//MethodInfo method = type.GetMethod("Print");
// method.Invoke (new_myclass_instance, null);
//3、通过Assembly获取程序集中类
//Assembly assembly = Assembly.LoadFile(@"/Users/ww/Desktop/MyDLL.dll");
//object new_person_instance = assembly.CreateInstance("MyDLL.MyClass");
////Console.WriteLine(new_person_instance);
//Type type = assembly.GetType("MyDLL.MyClass");
//MethodInfo method = type.GetMethod("Print");
// method.Invoke (new_person_instance, null);
//Assembly assembly = Assembly.GetExecutingAssembly(); // 获取当前程序集
//object obj = assembly.CreateInstance("Refelection.MainClass");
//((MainClass)obj).show();
}
}
}
Assembly.Load()方法,Assembly.LoadFrom()方法,Assembly.LoadFile()方法的区别!
1:Assembly.Load()
这个方法通过程序集的长名称(包括程序集名,版本信息,语言文化,公钥标记)来加载程序集的,会加载此程序集引用的其他程序集,一般情况下都应该优先使用 这个方法,他的执行效率比LoadFrom要高很多,而且不会造成重复加载的问题(原因在第2点上说明)使用这个方法的时候, CLR会应用一定的策略来查找程序集,实际上CLR按如下的顺序来定位程序集:
⑴如果程序集有强名称,首先在全局程序集缓存(GAC)中查找程序集。
⑵如果程序集的强名称没有正确指定或GAC中找不到,那么通过配置文件中的<codebase>元素指定的URL来查找
⑶如果没有指定强名称或是在GAC中找不到,CLR会探测特定的文件夹:
假设你的应用程序目录是C:\AppDir,<probing>元素中的privatePath指定了一个路径Path1,你要定位的程序集是AssemblyName.dll则CLR将按照
如下顺序定位程序集
C:\AppDir\AssemblyName.dll
C:\AppDir\AssemblyName\AssemblyName.dll
C:\AppDir\Path1\AssemblyName.dll
C:\AppDir\Path1\AssemblyName\AssemblyName.dll
如果以上方法不能找到程序集,会发生编译错误,如果是动态加载程序集,会在运行时抛出异常!
2:Assembly.LoadFrom()
这个方法从指定的路径来加载程序集,实际上这个方法被调用的时候,CLR会打开这个文件,获取其中的程序集版本,语言文化,公钥标记等信息,把他
们传递给 Load方法,接着,Load方法采用上面的策略来查找程序集。如果找到了程序集,会和LoadFrom方法中指定的路径做比较,如果路径相同,该程序集会被认为是应用程序的一部分,如果路径不同或Load方法没有找到程序集,那该程序集只是被作为一个“数据文件”来加载,不会被认为是应用程序的一部分。这就是在第1点中提到的Load方法比LoadFrom方法的执行效率高的原因。另外,由于可能把程序集作为“数据文件”来加载,所以使用 LoadFrom从不同路径加载相同程序集的时候会导致重复加载。当然这个方法会加载此程序集引用的其他程序集。
3:Assembly.LoadFile()
这个方法是从指定的文件来加载程序集,和上面方法的不同之处是这个方法不会加载此程序集引用的其他程序集!
结论:一般大家应该优先选择Load方法来加载程序集,如果遇到需要使用LoadFrom方法的时候,最好改变设计而用Load方法来代替!
另:Assembly.LoadFile 与 Assembly.LoadFrom的区别
1、Assembly.LoadFile只载入相应的dll文件,比如Assembly.LoadFile("abc.dll"),则载入abc.dll,假如abc.dll中引用了def.dll的话,def.dll并不会被载入。
Assembly.LoadFrom则不一样,它会载入dll文件及其引用的其他dll,比如上面的例子,def.dll也会被载入。
2、用Assembly.LoadFrom载入一个Assembly时,会先检查前面是否已经载入过相同名字的Assembly,比如abc.dll有两个版本(版本1在目录1下,版本2放在目录2下),程序一开始时载入了版本1,当使用Assembly.LoadFrom("2\\abc.dll")载入版本2时,不能载入,而是返回版本1。
Assembly.LoadFile的话则不会做这样的检查,比如上面的例子换成Assembly.LoadFile的话,则能正确载入版本2。
LoadFile:加载指定路径上的程序集文件的内容。LoadFrom: 根据程序集的文件名加载程序集文件的内容。
区别:
LoadFile 方法用来来加载和检查具有相同标识但位于不同路径中的程序集.但不会加载程序的依赖项。
LoadFrom 不能用于加载标识相同但路径不同的程序集。