C#和其他语言相比强不强大、优不优秀,可能1000个人会给出1001个答案
(¥@%……*&……%¥#),但有一点是肯定的,它借鉴了很多其它语言的一些优点,尤其是JAVA,比如所有的类都有一个最终基类--Object,比如框架的引入,比如JAVA的垃圾回收机制和反射机制(至于是否还有其它的一些技术,限于本人对这两门语言的理解,尤其对JAVA只是浅尝辄止,这里就不胡言乱语了)
说的极端点,现代计算机语言的发展,语言已经不是单纯的语言,而是集成在一个“环境”里的语言,而所谓的跨平台也不是该语言的跨平台,而是该“环境”的跨平台,程序只是和“环境”进行“交流”,“环境”再和操作系统去“交流”,相当于多了一个中间层,“环境”和程序互不透明。对于JAVA,这个环境是JAVA虚拟机,c#,当然就是.NET框架了(呵呵,不知道.net框架是否支持其它非Microsoft系的操作系统)。
有点跑题了哈~言归正传,C#的程序分为三个部分,程序集(assembly)、模块
(module)、类型(class)三部分组成,反射则提供一种编程方式,使程序能够在运行过程中动态获得这三个部分的信息,并能够动态生成实例、调用方法,c#是怎么做到这点的需要深入到.net框架底层去探悉内在的运行机制了,这部分至今我没见过像以前VC的《深入浅出MFC》那种深度的书籍,可能是微软没有公开,也许深入研究java架构有可能会寻到些踪迹。
反射具体到实现上,是Systems.reflection命名空间的类和Systems.Type类协同完成的,其中Type类是整个Reflection的根,是反射技术的核心。Systems.Reflection 在MSDN中的解释如下:包含通过检查托管代码中的程序集、模块、成员、参数和其它实体的元数据来检索其相关信息的类型,常用的几个类 Modules、MethodInfo、PropertyInfo、EventInfo等。这些类型还可用于操作加载类型的实例。下面定义一个简单的控制台程序,添加如下代码:
foreach(Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) {
Modules []modules = assem.GetModules();
Module module = assem.GetModule(“程序集名称”) }
在遍历中设置断点,运行调试,注意局部变量窗口中的信息。有点乱是不是,呵呵,包含了所有使用到的相关程序集的所有信息。
下面轮到Type类了,Type类的成员用于获取关于类型声明的信息,这些信息包括为类型定义的所有构造器、方法、字段、特性和事件,以及类所在的模块和配件, Type对象可以代表以下任何类型:类、值类型、数组、接口、指针和枚举。下面一个完整的例子来说明一下这几个类能做什么。
新建一个dll文件添加方法代码如下:
public class DllCalInt {
public int add(int a, int b) {
return a + b; }
public int sub(int a, int b)
{
return a - b; }
public int mul(int a, int b) {
return a * b; }
public int div(int a, int b) {
if (b != 0)
return a / b; else
return 0; } }
public class DllCalDouble {
public double add(double a, double b) {
return a + b; }
public double sub(double a, double b) {
return a - b; }
public double mul(double a, double b) {
return a * b; }
public double div(double a, double b) {
if (b != 0)
return a / b; else
return 0; }
}
建立一个控制台程序,代码如下:
static void Main(string[] args) {
Assembly asm = Assembly.LoadFile(\".......\\\\DllCal.dll\"); Type []type = asm.GetTypes(); foreach (Type tp in type) {
MethodInfo[] md = tp.GetMethods(); }
}
设置断点,运行调试。观察局部变量窗口中的type、md数组。所有的类、方法都反射出来了吧?--每个类都多了四个方法,这就是上面说的c#所有类的最终基类是Object,多出的四个方法是从Object中继承的。
类、方法都能够动态获得,但是怎么动态调用呢?--C#提供了几种动态调用的方法 1. System.Activator 的CreateInstance方法。该方法返回新对象的引用。
2. System.Activator 的createInstanceFrom 与上一个方法类似,不过需要指定类型及其程序集
3. System.Appdomain 的方法:createInstance, CreateInstanceAndUnwrap,
CreateInstranceFrom 和 CreateInstraceFromAndUnwrap(需要了解关于c#程序域的相关知
识)
4. System.type的InvokeMember实例方法:这个方法返回一个与传入参数相符的构造函数,并构造该类型。
5. System.reflection.constructinfo 的Invoke实例方法
这里不一个个介绍了,只做一个简单的调用,把main函数改成如下方式
static void Main(string[] args) {
Assembly asm = Assembly.LoadFile(\"......\\\\DllCal.dll\"); Type []type = asm.GetTypes(); foreach (Type tp in type) {
MethodInfo[] md = tp.GetMethods();
object obj = Activator.CreateInstance(tp); for (int i = 0; i < (md.Length-4); i++) {
ParameterInfo[] pInfo = md[0].GetParameters(); int nPara = pInfo.Length;
object []objPara = new object[nPara]; for (int j = 0; j < nPara; j++) objPara[j] = j + 8;
Console.WriteLine((md[i].Invoke(obj, objPara)).ToString()); }
Console.WriteLine(\"Module finished!\"); } }
运行单步调试或在第一条语句处设置断点逐步执行,观察程序运行状态,呵呵,试一
下就全都明白了。
这里,只是一个最简单的演示,权当抛砖引玉。用反射能实现很多有趣的功能,包括插件技术等,不过得好好翻翻MSDN,好好研究下System.Reflections命名空间中的几个类了。微软的技术有个特点,刚开始入门时好像很容易,看个几个小时就可能会使用了,但真要深入研究或者要充分发挥该技术的功能时,就会变得很麻烦,需要花费很多的时间和精力去研究一堆堆的类库,反射也是如此。没办法,微软把它的技术都裹得严严实实的,你需要做的只是研究它的使用文档、研究一堆堆的方法、属性、事件等东西,至于创造性嘛~那是微软的事儿了,你甚至不需要明白原理,死记使用步骤就行了,呵呵,逐步的,程序员就消失了,取而代之的是“微软安装工人”。
反射很灵活,但是是以牺牲性能来实现的,使用反射时CLR需要进行权限检查、参数校验等一堆工作,在进行大批量数据操作时,速度比较慢,所以使用反射技术需要慎重。 下面来谈一下动态编译技术,把这两部分放到一起是因为反射是动态编译的一个先决条件,而且具有某些相似性。
所谓的动态编译,就是程序在运行过程中自动生成(或者自动加载)代码,然后调用.NET框架提供的C#程序编译器生成临时的程序集,再将临时的程序集加载到应用程序域中动态的调用其中的对象模块,直白点说就是,你的程序就是一个编译器。动态编译的程序在执行过编译后,程序效率同在代码中直接引用dll是相同的,它在临时文件夹中生成一个临时的代码和编译文件。
具体到实现上就是,对System.CodeDom.Compiler和Microsoft.CSharp两个名称空间
中类的使用,并用到反射来动态执行方法。使用动态编译的一个简单的过程可以总结为如下几步:
1、获得进行编译的代码(呵呵,废话多了点) 2、声明CSharpCodeProvider实例 3、设置调用编译器的相关参数
4、调用CSharpCodeProvider实例的CompilerAssembleFromSource方法,程序会在过程中生成一些临时文件(可能会受到.Net框架的安全设置影响,不是太清楚....)
5、应用程序通过反射生成动态编译的对象 6、调用方法 7、任务完成 :->
写了一个简单的小程序,实现动态编译。
using System;
using System.Collections.Generic; using System.Linq; using System.Text;
using System.Reflection; using Microsoft.CSharp; using System.CodeDom;
using System.CodeDom.Compiler; using System.IO;
namespace TestRF {
class Program {
static void Main(string[] args) {
#region dynamic compiler //1 读取代码文件
StreamReader sr = new StreamReader(@\"E:\\works\\TestRF\\code.txt\"); string strCode = \"\"; string strTemp = \"\";
while ((strTemp=sr.ReadLine()) != null) {
strCode += strTemp+\"\\r\\n\"; }
//2 建立CSharpCodeProvider 对象实例
CSharpCodeProvider provider = new CSharpCodeProvider(); //3 配置编译参数
CompilerParameters comParas = new CompilerParameters(); comParas.ReferencedAssemblies.Add(\"System.dll\"); comParas.GenerateExecutable = false; comParas.GenerateInMemory = true;
//4 编译
CompilerResults comRes = provider.CompileAssemblyFromSource(comParas,strCode); //4 判断编译过程是否正确 if (comRes.Errors.HasErrors) {
Console.WriteLine(\"There is something wrong,please check it first!\");
} else {
//5 利用反射调用方法
Assembly asm = comRes.CompiledAssembly;
object obj = asm.CreateInstance(\"Test.DynamicCompile\"); MethodInfo mInfo = obj.GetType().GetMethod(\"HelloWorld\"); //5
Console.WriteLine(mInfo.Invoke(obj,null));
}
Console.ReadLine(); } } }
\"E:\\works\\TestRF\\code.txt\"内容如下
using System;
using System.Collections.Generic; using System.Text; namespace Test {
public class DynamicCompile {
public void HelloWorld() {
Console.WriteLine(\"Hello World!\"); } } }
上面的程序从文本中读取一段代码,执行动态编译,然后通过反射调用方法。因为代码在程序执行时进行编译动作,所以第一次执行速度和你用VS编译一段程序的时间差不多,以后程序再次调用该方法时,就和正常调用dll的速度差不多了(呵呵,我还没试过也没想过怎样用动态编译做一个完整的程序,以测试动态编译的速度,这是网上说)。
以上只是一些这两个技术的简单演示(程序代码都测试过),如果真需要使用反射或动态编译技术来实现具体的应用,需要完善和学习的地方还很多。
水平有限,写的也仓促,没有考校,肯定会有错误的地方,希望大家指教。
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- sceh.cn 版权所有 湘ICP备2023017654号-4
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务