引言:每隔10年左右,编程人员就需要花费大量的时间和精力去学习新的编程技术。在80年代是Unix和C,90年代是Windows和C++,现在又轮到了微软的.NETFramework和C#。尽管需要学习新的技术,但由此带来的好处却远高于付出的劳动。幸运的是,使用C#和.NET进行的大多数工程的分析和设计与在C++和Windows中没有本质的变化。在本篇文章中,我将介绍如何实现由C++到C#的飞跃。 已经有许多文章介绍过C#对C++的改进,在这里我就不再重复这些问题了。在这里,我将重点讨论由C++转向C#时最大的变化:由不可管理的环境向可管理的环境的变化。此外,我还会提出一些C#编程人员容易犯的错误供大家参考,此外,还将说明一些C#语言的能够影响编程的新功能。 系列文章:[由C++转向C#需要注意的变化(一)(二)(三)(四)] 属性的使用 为了对属性进行测试,我们创建一个名字为MyMath的简单类,并给它添加二个函数,然后给它指定bugfix属性。
| [BugFixAttribute(121,"JesseLiberty","01/03/05")] [BugFixAttribute(107,"JesseLiberty","01/04/05", Comment="Fixedoffbyoneerrors")] publicclassMyMath |
这些数据将与元数据存储在一起。下面是完整的源代码及其输出: 自定义属性
| usingSystem; //创建被指派给类成员的自定义属性 [AttributeUsage(AttributeTargets.Class, AllowMultiple=true)] publicclassBugFixAttribute:System.Attribute { //位置参数的自定义属性构造器 publicBugFixAttribute (intbugID, stringprogrammer, stringdate) { this.bugID=bugID; this.programmer=programmer; this.date=date; } publicintBugID { get { returnbugID; } } //命名参数的属性 publicstringComment { get { returncomment; } set { comment=value; } } |
| publicstringDate { get { returndate; } } publicstringProgrammer { get { returnprogrammer; } } //专有成员数据 privateintbugID; privatestringcomment; privatestringdate; privatestringprogrammer; } //把属性指派给类 [BugFixAttribute(121,"JesseLiberty","01/03/05")] [BugFixAttribute(107,"JesseLiberty","01/04/05", Comment="Fixedoffbyoneerrors")] publicclassMyMath { publicdoubleDoFunc1(doubleparam1) { returnparam1+DoFunc2(param1); } publicdoubleDoFunc2(doubleparam1) { returnparam1/3; } } publicclassTester { publicstaticvoidMain() { MyMathmm=newMyMath(); Console.WriteLine("CallingDoFunc(7).Result:{0}", mm.DoFunc1(7)); } } |
输出:
| CallingDoFunc(7).Result:9.3333333333333339 |
象我们看到的那样,属性对输出绝对没有影响,创建属性也不会影响代码的性能。到目前为止,读者也只是在听我论述有关属性的问题,使用ILDASM浏览元数据,就会发现属性确实是存在的。 映射 在许多情况下,我们需要一种方法,能够从元数据中访问属性,C#提供了对映射的支持以访问元数据。通过初始化MemberInfo类型对象,System.Reflection名字空间中的这个对象可以用来发现成员的属性,对元数据进行访问。
| System.Reflection.MemberInfoinf=typeof(MyMath); |
对MyMath类型调用typeof操作符,它返回一个由继承MemberInfo而生成的Type类型的变量。 下一步是对MemberInfo对象调用GetCustomAttributes,并将希望得到的属性的类型作为一个参数传递给GetCustomAttributes。我们将得到一个对象数组,数组的每个成员的类型都是BugFixAttribute。
| object[]attributes; attributes=Attribute.GetCustomAttributes(inf,typeof(BugFixAttribute)); |
我们就可以遍历这个数组了,打印BugFixAttribute对象的数组,代码下所示: 属性的打印
| publicstaticvoidMain() { MyMathmm=newMyMath(); Console.WriteLine("CallingDoFunc(7).Result:{0}", mm.DoFunc1(7)); //获取成员信息并使用它访问自定义的属性 System.Reflection.MemberInfoinf=typeof(MyMath); object[]attributes; attributes= Attribute.GetCustomAttributes(inf,typeof(BugFixAttribute)); //遍历所有的属性 foreach(Objectattributeinattributes) { BugFixAttributebfa=(BugFixAttribute)attribute; Console.WriteLine("\nBugID:{0}",bfa.BugID); Console.WriteLine("Programmer:{0}",bfa.Programmer); Console.WriteLine("Date:{0}",bfa.Date); Console.WriteLine("Comment:{0}",bfa.Comment); } } |
类型发现 我们可以通过映象的方法来研究一个组合实体的内容,如果要建立需要显示组合体内部信息的工具或动态地调用组合体中的途径,这一方法是非常有用的。 通过映象的方法,我们可以知道一个模块、方法、域、属性的类型,以及该类型的每个方法的信号、该类支持的界面和该类的超级类。我们可以通过如下的形式,用Assembly.Load静态方法动态地加载一个组合体:
| publicstaticAssembly.Load(AssemblyName) |
然后,可以将它传递到核心库中。
| Assemblya=Assembly.Load("Mscorlib.dll"); |
一旦加载了组合体,我们可以通过调用GetTypes返回一个Type对象数组。Type对象是映射的核心,它表示类、界面、数组、值和枚举等的类型定义。
| Type[]types=a.GetTypes(); |
组合休会返回一个类型的数组,我们可以使用foreach-loop结构显示该数组,其输出将有好几页文档之多,下面我们从中找一小段:
| TypeisSystem.TypeCode TypeisSystem.Security.Util.StringExpressionSet TypeisSystem.Text.UTF7Encoding$Encoder TypeisSystem.ArgIterator TypeisSystem.Runtime.Remoting.JITLookupTable 1205typesfound |
我们得到了一个内容为核心库中类型的数组,可以将它们都打印出来,该数组将有1205个项。 对一种类型映射我们也可以对组合体中一种类型进行映射。为此,我们可以使用GetType方法从组合体中解析出一个类型:
| publicclassTester { publicstaticvoidMain() { //检查一个对象 TypetheType=Type.GetType("System.Reflection.Assembly"); Console.WriteLine("\nSingleTypeis{0}\n",theType); } } |
输出如下所示:
| SingleTypeisSystem.Reflection.Assembly |
发现成员 我们还可以得到所有成员的类型,显示所有的方法、属性、域,下面的代码演示了实现上述目标的代码。
| Figure9GettingAllMembers publicclassTester { publicstaticvoidMain() { //检查一个单一的对象 TypetheType=Type.GetType("System.Reflection.Assembly"); Console.WriteLine("\nSingleTypeis{0}\n",theType); //获取所有的成员 MemberInfo[]mbrInfoArray= theType.GetMembers(BindingFlags.LookupAll); foreach(MemberInfombrInfoinmbrInfoArray) { Console.WriteLine("{0}isa{1}", mbrInfo,mbrInfo.MemberType.Format()); } } } |
尽管得到的输出还非常长,但在输出中我们可以得到如下面的不甘落后民示的域、方法、构造器和属性:
| System.Strings_localFilePrefixisaField BooleanIsDefined(System.Type)isaMethod Void.ctor()isaConstructor System.StringCodeBaseisaProperty System.StringCopiedCodeBaseisaProperty |
|