反射的基础概念:
反射(Reflection)是.NET中的重要机制,通过放射,可以在运行时获得.NET中每一个类型(包括类、结构、委托、接口和枚举等)的成员,包括方法、属性、事件,以及构造函数等。还可以获得每个成员的名称、限定符和参数等。有了反射,即可对每一个类型了如指掌。如果获得了构造函数的信息,即可直接创建对象,即使这个对象的类型在编译时还不知道。
NET可执行应用程序结构 :
程序代码在编译后生成可执行的应用,我们首先要了解这种可执行应用程序的结构。
应用程序结构分为应用程序域—程序集—模块—类型—成员几个层次,公共语言运行库加载器管理应用程序域,这种管理包括将每个程序集加载到相应的应用程序域以及控制每个程序集中类型层次结构的内存布局。
程序集包含模块,而模块包含类型,类型又包含成员,反射则提供了封装程序集、模块和类型的对象。我们可以使用反射动态地创建类型的实例,将类型绑定到现有对象或从现有对象中获取类型,然后调用类型的方法或访问其字段和属性。反射通常具有以下用途。
(1)使用Assembly定义和加载程序集,加载在程序集清单中列出模块,以及从此程序集中查找类型并创建该类型的实例。
(2)使用Module了解包含模块的程序集以及模块中的类等,还可以获取在模块上定义的所有全局方法或其它特定的非全局方法。
(3)使用ConstructorInfo了解构造函数的名称、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。使用Type的GetConstructors或GetConstructor方法来调用特定的构造函数。
(4)使用MethodInfo了解方法的名称、返回类型、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。使用Type的GetMethods或GetMethod方法来调用特定的方法。
(5)使用FiedInfo了解字段的名称、访问修饰符(如public或private)和实现详细信息(如static)等,并获取或设置字段值。
(6)使用EventInfo了解事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序。
(7)使用PropertyInfo了解属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,获取或设置属性值。
(8)使用ParameterInfo了解参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中的位置等。
System.Reflection.Emit命名空间的类提供了一种特殊形式的反射,可以在运行时构造类型。
反射也可用于创建称为类型浏览器的应用程序,使用户能够选择类型,然后查看有关选定类型的信息。
此外,Jscript等语言编译器使用反射来构造符号表。System.Runtime.Serialization命名空间中的类使用反射来访问数据并确定要永久保存的字段,System.Runtime.Remoting命名空间中的类通过序列化来间接地使用反射。
( 1 ) 反射什么时候可以使用:
使用反射工厂的优点是极大地减少了工厂类的数量、降低了代码的冗余,并且系统更容易扩展,在增加新类型后,不需要修改工厂类。理论上可以用一个工厂完成很多类型的实例化。多类型的实例化可以通过反射轻松实现。
但是,相应地反射得消耗是非常大的,使用时应该首先考虑清楚这一点。
( 2 )反射入门(一些简单实例)
实例1 :
在程序去得时动态实例化对象,获得对象的属性,并调用对象的方法。
1Namespace ReflectionExample
2{
3 class Class1
4 {
5 [STAThread]
6 static void Main (string [ ] args)
7 {
8 System.Console.WriteLine(“列出程序集中的所有类型”);
9 Assembly a = Assembly.LoadFrom (“ReflectionExample.exe”);
10 Type[ ] mytypes = a.GetTypes( );
11
12 Foreach (Type t in mytypes) //循环所有类型Type
13 {
14 System.Console.WriteLine ( t.Name );
15 }
16 System.Console.ReadLine ( );
17 System.Console.WriteLine (“列出HellWord中的所有方法” );
18 Type ht = typeof(HelloWorld);
19 MethodInfo[] mif = ht.GetMethods();
20 foreach(MethodInfo mf in mif) //循环所有方法MethodInfo[]
21 {
22 System.Console.WriteLine(mf.Name);
23 }
24 System.Console.ReadLine();
25 System.Console.WriteLine("实例化HelloWorld,并调用SayHello方法");
26 Object obj = Activator.CreateInstance(ht);
27 string[] s = {"zhenlei"};
28 Object bojName = Activator.CreateInstance(ht,s); // 实例化CreateInstance
29 BindingFlags flags = (BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.Static|BindingFlags.Instance|BindingFlags.DeclaredOnly);
30 MethodInfo msayhello = ht.GetMethod("SayHello");
31 msayhello.Invoke(obj,null); //调用方法Invoke
32 msayhello.Invoke(objName,null);
33 System.Console.ReadLine();
34 }
35 }
36}
1using System; //被使用的Dll
2namespace ReflectionExample
3{
4 public class HelloWorld
5 {
6 string myName = null;
7 public HelloWorld(string name)
8 {
9 myName = name;
10 }
11 public HelloWorld() : this(null)
12 {}
13 public string Name
14 {
15 get
16 {
17 return myName;
18 }
19 }
20 public void SayHello()
21 {
22 if(myName == null)
23 {
24 System.Console.WriteLine("Hello World");
25 }
26 else
27 {
28 System.Console.WriteLine("Hello," + myName);
29 }
30 }
31 }
32}
33
实例2:
用反射调用任意.net库中的方法(函数中含详尽解述)
函数如下:ReturnMessage自定议类
2、通过NameSpaceAndClassName和MethodName实际上就可以精确定位一个方法了如调用abc.dll里的namespace1.Class1.Main,调用起来就是CallAssembly("abc.dll","namespace1.Class1","Main",null)
public static ReturnMessage CallAssembly(string Path,string NameSpaceAndClassName,string MethodName,object[] Parameters)
{
try
{
Assembly Ass=Assembly.LoadFrom(Path);//调入文件(不限于dll,exe亦可,只要是.net)
Type TP=Ass.GetType(NameSpaceAndClassName);//NameSpaceAndClassName是"名字空间.类名",如"namespace1.Class1"
MethodInfo MI=TP.GetMethod(MethodName);//MethodName是要调用的方法名,如"Main"
object MeObj=System.Activator.CreateInstance(TP);
MI.Invoke(MeObj,Parameters);//Parameters是调用目标方法时传入的参数列表
return new ReturnMessage(true,"成功调用",1);
}
catch(Exception e)
{
return new ReturnMessage(false,"出现异常,消息为:"+e.Message,-1,e);
}
}
实例3:
(1)namespace ClassLibrarySport
{
public abstract class Sport
{
protected string name;
public abstract string GetName();
public abstract string GetDuration();
}
}
= = = = = == = == = == = == = == = == = == = == = == = == = == = == = == = == =
(2)
namespace ClassLibrarySomeSports//该项目添加了对(1)的引用
{
public class Football : ClassLibrarySport.Sport
{
public Football()
{
name = "Football";
}
public override string GetName()
{
return name;
}
public override string GetDuration()
{
return "four 15 minute quarters";
}
}
}
= = = = = == = == = == = == = == = == = == = == = == = == = == = == = == = == =
(3)namespace ConsoleAssemblyTest//该项目添加了对(1)的引用
{
class Program
{
static void Main(string[] args)
{
Assembly assembly = Assembly.LoadFrom(@"E:/ClassLibrarySomeSports/bin/Debug/ClassLibrarySomeSports.dll");
Type[] types = assembly.GetTypes();
Console.WriteLine("Get Type From ClassLibrarySomeSports.dll:");
for (int i = 0; i < types.Length; i++)
{
Console.WriteLine(types[i].Name);//通过反射获取dll类型
}
//使用GetConstructor()方法获取对应类型的构造器,从而构造出该类型的对象
Console.WriteLine("Use Method GetConstructor():");
ConstructorInfo ci = types[0].GetConstructor(new Type[0]);
ClassLibrarySport.Sport sport = (ClassLibrarySport.Sport)ci.Invoke(new object[0]);
Console.WriteLine(sport.GetName() + " has " + sport.GetDuration());
//使用Activator.CreateInstance()方法构造出该类型的对象
//使用assembly.CreateInstance()返回为null
Console.WriteLine("Use Method CreateInstance():");
ClassLibrarySport.Sport sport1 = (ClassLibrarySport.Sport)
Activator.CreateInstance(types[0]);
Console.WriteLine(sport1.GetName() + " has " + sport1.GetDuration());
//反射指定类型中的名称为“GetDuration”的方法,通过Invoke()方法执行该方法
object objSport = Activator.CreateInstance(types[0]);
MethodInfo method = types[0].GetMethod("GetDuration");
object o = method.Invoke(objSport, new object[0]);
Console.WriteLine(o as string);
Console.Read();
}
}
}
= = = = = == = == = == = == = == = == = == = == = == = == = == = ==
Output://输出结果
Get Type From ClassLibrarySomeSports.dll:
Football
Use Method GetConstructor():
Football has four 15 minute quarters
Use Method CreateInstance():
Football has four 15 minute quarters
four 15 minute quarters
( 3 ) 简单谈谈反射工厂
借网上一个实例,在此再简单论述一下反射在工厂三层中的应用,以作深一步理解使用反射。
例:假设我们需要创建一种交通工具,可以是汽车、火车或者轮船。
采用简单工厂方法来实现,代码如下:
` 采用接口定义了抽象的工厂方法
public Interface CreateVehicleInterface CreateVehicle ` 定义一个产生交通工具的接口
Function CreateAVehicle()Function CreateAVehicle() As Vehicle `创建一个交通工具
End Interface
` 具体的创建由子类决定
public Class CreateCarClass CreateCar
Implements CreateCar
Public Function CreateAVheicle()Function CreateAVheicle() AsVehicle Implements
CreateVehicle.CreateAVehicle
Return New Car
End Function
End Class
如果我们希望增加一个新的交通工具,需要实现工具接口和产生交通工具的工厂方法。
1.一般工厂做法:
Public Class CreateBoatClass CreateBoat
Implements CreateVehicle
Public Function CreateAVehicle()Function CreateAVehicle() As Vehicle Implements
CreateVehicle.CreateAVehicle
Return New Boat
End Function
End Class
2.采用反射技术简化工厂类
1.首先查看采用反射技术实现简化的实例:
Imports System.Reflection
Public Class CreateVehicleByTypeClass CreateVehicleByType
Implements CreateVehicle
Private VeicleType As Type
Public Sub New()Sub New(ByVal t As Type)
VeicleType = t
End Sub
Public Function CreateAVehicle()Function CreateAVehicle() As Vehicle Implements
CreateVehicle.CreateAVehicle
Dim objConstructor As ConstructorInfo =
VeicleType.GetConstructor(System.Type.EmptyTypes)
Dim c As Vehicle = Ctype(objConstructou.Invoke(Nothing),Vehicle)
Return c
End Function
End Class
2. 在使用时,只要在创建时带入需要创建的类的类型:
Private Sub btcreateByType_Click()Sub btcreateByType_Click(ByVal sender As System.Object,ByVal e As
System.EventArgs) Handles btCreateBytype.Clik
`根据选择创建一个交通工具并执行GO
Dim v As Vehicle `我们不知道需要创建的具体交通工具
Dim f As CreateVehicle
If rCar.Checked Then
F = New CreateVehicleByType(GetType(car))
End If
If rTrain.Checked Then
F = New CreateVehicleByType(GetType(Train))
End If
If rBoat.Checked Then
F = New CreateVehicleByType(GetType(Boat))
End If
If rBus.Checked Then
F = New CreateVehicleByType(GetType(Boat))
End If
V = f.CreateAVehicle
`执行GO指令
lbGO.Text = v.Go
End Sub
3.对简单工厂的改进
简单工厂通过参数决定创建的类型,这些参数是在编程时预设的。因此在编译后就无法修改,让我们回顾代码:
我们可以将这个工厂改造为反射工厂://这里不是太明_下面代码如何体现反射工厂的作用
Public Class clsCreateDBClass clsCreateDB
Public Shared Function CreateDB()Function CreateDB(ByVal strType As string, ByVal strConnString AsString) As _ clsAbstractDB
Select Case strType.ToUpper
Case “ORACLE”
Dim myOracle As clsoracleDB
MyOracle = New clsOracleDB(strConnString)
Return myOracle
Case “SQLSERVER”
Dim mysqlserver As clsSQLServerDB
Mysqlserver = New clsSQLServerDB(strConnString)
Return mysqlserver
Case Else
Dim myoledb As clsOLEDB
Myoledb = New clsOLEDB(strConnString)
Return myoledb
End Select
End Function
End Class
4.反射与工厂方法
如果工厂方法仅仅是为了获得某个产品的实例,那么完全可以使用反射技术来实现工厂方法。这样解决了工厂方法的潜在问题,即当增加产品类时,必须增加相应的子类。
然而当工厂方法所存在的类不仅是实例化产品时,采用反射不一定是好办法,因为可能使问题变得复杂。
5.反射与抽象工厂
可以采用反射来实现抽象工厂,这时抽象工厂可能变成了使用反射技术的具体工厂,不再有子类存在。创建交通系统的实例可以用如下的代码来写:
'<summary>
'VehicleSystemReflectionFactory 采用反射技术的工厂。
'</summary>
Public Class VehicleSystemReflectionFactoryClass VehicleSystemReflectionFactory
Dim vehicleType As String
Dim vehicleStationType As String
Public Sub New()Sub New(ByVal vt As String, ByVal vst As String)
Me.vehicleType = vt
Me.vehicleStationType = vst
End Sub
Public Function GetVehicle()Function GetVehicle() As Vehicle
Return CType(createbytype(Me.vehicleType), Vehicle)
End Function
Public Function GetVehicleStation()Function GetVehicleStation() As VehicleStation
Return CType(createbytype(Me.vehicleStationType), VehicleStation)
End Function
Private Function createbytype()Function createbytype(ByVal vt As String) As Object
Dim tt As Type
tt = Type.GetType(vt)
Dim ci As ConstructorInfo
ci = tt.GetConstructor(System.Type.EmptyTypes)
Dim null As System.DBNull
Return ci.Invoke(null)
End Function
End Class
6.反射工厂的使用效果
使用反射工厂的优点是极大地减少了工厂类的数量、降低了代码的冗余,并且系统更容易扩展,在增加新类型后,不需要修改工厂类。
使用反射工厂的代价是工厂与产品之间的依赖关系不明显,由于是动态绑定,因此理论上可以用一个工厂完成很多类型的实例化,从而使得代码 不容易理解。另外增大了测试难度,创建是动态完成的,测试用例的编写和测试执行要比传统的工厂困难。