反射是.net中的高级功能之一,利用反射可以实现许多以前看来匪夷所思的功能,下面是我看了《Programming C#》(O'Reilly)之后对于反射的一点实践,本想直接做个应用程序来说明问题,但苦于工作繁忙并考虑到以简单为主,故先对反射发送(reflection emit)的使用做一些介绍。文章最后再给出一个实例。
下面的程序在运行时生成了一个Test.cs文件,并调用csc编译成Test.dll文件,然后利用Type.InvokeMember()方法调用其中的SayHello()方法,然后和原始方法对比一下性能。
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
namespace InvokeMember
{
///
/// Class1 的摘要说明。
///
class Class1 { ///
/// 应用程序的主入口点。
///
[STAThread]
static void Main( string [] args)
{
// 循环次数
const int iterations = 100 ;
// 计算所用时间
DateTime startTime = DateTime.Now;
for ( int i = 0 ;i < iterations;i ++ ) { // 对照方法
Console.WriteLine( " Hello,World " );
}
TimeSpan elasped = DateTime.Now - startTime;
Console.WriteLine( " Looping Elapsed milliseconds: " + elasped.TotalMilliseconds + " for {0} iterations " ,iterations);
// 使用反射发送
ReflectionTest t = new ReflectionTest();
// 计算所用时间
startTime = DateTime.Now;
for ( int i = 0 ;i < iterations;i ++ )
{
t.DoOperation();
}
elasped = DateTime.Now - startTime;
Console.WriteLine( " Looping Elapsed milliseconds: " + elasped.TotalMilliseconds + " for {0} iterations " ,iterations);
Console.ReadLine();
}
}
///
/// Reflection 的摘要说明。
///
public class ReflectionTest
{
// 保存动态生成并编译的类的type对象
Type theType = null ;
// 保存动态生成类的实例
object theClass = null ;
///
/// 供Client调用的方法
///
public void DoOperation()
{
// 未初始化
if (theType == null )
{
// 初始化
GenerateCode();
}
// 调用方法时的参数数组(此处为空)
object [] arguments = new object [ 0 ];
// 调用动态生成类的方法
theType.InvokeMember( " SayHello " , // 要调用的方法名
BindingFlags.Default | BindingFlags.InvokeMethod, // Binding标志,具体参看msdn
null , // 使用默认Binding对象
theClass, // 在theClass实例上调用此方法
arguments // 调用方法时的参数数组
);
}
///
/// 运行时生成代码
///
private void GenerateCode()
{
// 文件名
string fileName = " Test " ;
// 打开文件,如果不存在,则创建
Stream s = File.Open(fileName + " .cs " ,FileMode.Create);
// 创建一个StreamWriter来写入数据
StreamWriter wrtr = new StreamWriter(s);
// 写入动态创建类的源代码
wrtr.WriteLine( " // 动态创建Test类 " );
// 类名
string className = " TestClass " ;
wrtr.WriteLine( " using System; " );
wrtr.WriteLine( " class {0} " ,className);
wrtr.WriteLine( " { " );
wrtr.WriteLine( " /tpublic void SayHello() " );
wrtr.WriteLine( " /t{ " );
wrtr.WriteLine( " /t/tConsole.WriteLine(/"Hello,World/"); " );
wrtr.WriteLine( " /t} " );
wrtr.WriteLine( " } " );
// 关闭StreamWriter和文件
wrtr.Close();
s.Close();
// 启动进程编译源文件
// 指定参数
ProcessStartInfo psi = new ProcessStartInfo();
// 启动cmd.exe
psi.FileName = " cmd.exe " ;
// cmd.exe的参数,/c-close,完成后关闭;后为参数,指定cmd.exe使用csc来编译刚才生成的源文件
string compileString = " /c C://WINNT//Microsoft.NET//Framework//v1.1.4322//csc.exe /optimize+ /target:library {0}.cs " ;
psi.Arguments = String.Format(compileString,fileName);
// 运行时的风格-最小化
psi.WindowStyle = ProcessWindowStyle.Minimized;
// 启动进程
Process proc = Process.Start(psi);
// 指定当前在此进程退出前等待
proc.WaitForExit();
// 从编译好的dll文件load一个Assembly
Assembly a = Assembly.LoadFrom(fileName + " .dll " );
// 创建类的实例
theClass = a.CreateInstance(className);
// 取得此类实例的类型
theType = a.GetType(className);
// 删除源文件
// File.Delete(flieName + ".cs");
}
}
}
程序运行结果:
Hello,World
Hello,World
.
.
.
Looping Elapsed milliseconds:93.75 for 100 iterations
Hello,World
Hello,World
.
.
.
Looping Elapsed milliseconds:2875 for 100 iterations
性能上不占优势,主要是因为要进行写文件和编译的工作,但是后面的方法会对性能进行优化,到最后一个方法时性能会有大幅提高,但是最后一种方法的实用性不如前两种。