引言:每隔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: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("
BugID:{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("
SingleTypeis{0}
",theType);
}
}
輸出如下所示:
SingleTypeisSystem.Reflection.Assembly
發現成員
我們還可以得到所有成員的類型,顯示所有的方法、屬性、域,下面的代碼演示了實現上述目標的代碼。
Figure9GettingAllMembers
publicclassTester
{
publicstaticvoidMain()
{
//檢查一個單一的對象
TypetheType=Type.GetType("System.Reflection.Assembly");
Console.WriteLine("
SingleTypeis{0}
",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