ASP.NET技術(shù)FAQ
發(fā)表時間:2024-02-18 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]1. 簡介 1.1 什么是 .NET? 1.2 .NET 只是 Windows DNA 的一個新名字嗎? 1.3 .NET 只適用于建立 Web 網(wǎng)站嗎? 1.4 .NET 是在什么時候宣布的? 1.5 .NET 將在何時發(fā)布? 1.6 如何開發(fā) .NET 應(yīng)用程序 1.7 可以從哪里下載 .NE...
1. 簡介
1.1 什么是 .NET?
1.2 .NET 只是 Windows DNA 的一個新名字嗎?
1.3 .NET 只適用于建立 Web 網(wǎng)站嗎?
1.4 .NET 是在什么時候宣布的?
1.5 .NET 將在何時發(fā)布?
1.6 如何開發(fā) .NET 應(yīng)用程序
1.7 可以從哪里下載 .NET SDK 和 Visual Studio 7?
1.8 .NET 中的關(guān)鍵技術(shù)是什么?
1.9 .NET 框架將運(yùn)行在什么平臺上?
1.10 .NET 框架支持什么語言?
1.11 .NET 框架符合標(biāo)準(zhǔn)化趨勢嗎?
2. 基本術(shù)語
2.1 什么是 CLR?
2.2 什么是 CTS?
2.3 什么是 CLS?
2.4 什么是 IL?
2.5 什么是 C#?
2.6 在 .NET 范疇里,“被管理”是什么含義?
2.7 什么是映像?
3. 元件
3.1 什么是元件?
3.2 怎樣創(chuàng)建元件?
3.3 私有元件和共享元件有什么不同?
3.4 元件如何相互找到?
3.5 元件版本如何起作用?
4. 應(yīng)用程序域
4.1 什么是應(yīng)用程序域?
4.2 如何創(chuàng)建 AppDomain?
4.3 我能編寫自己的 .NET 宿主嗎?
5. 垃圾收集
5.1 什么是垃圾收集?
5.2 對對象的最后一個引用撤銷后,它并不一定立即被破壞,對嗎?
5.3 .NET 為什么不提供確定化的析構(gòu)?
5.4 在 .NET 中缺少確定化的析構(gòu)有問題嗎?
5.5 確定化的析構(gòu)是否影響在被管理代碼中使用 COM 對象?
5.6 我聽說應(yīng)該避免使用 Finalize 方法,那么是否應(yīng)該在我的類里實(shí)現(xiàn) Finalize?
5.7 我有控制垃圾收集算法的手段嗎?
5.8 我怎么知道垃圾收集器在做什么?
6. 屬性
6.1 什么是屬性?
6.2 我能創(chuàng)建自己的 metadata 屬性嗎?
6.3 我能創(chuàng)建自己的 context 屬性嗎?
7. 代碼訪問安全性
7.1 什么是代碼訪問安全性 (CAS)?
7.2 CAS 如何起作用?
7.3 誰定義 CAS 代碼組?
7.4 如何定義自己的代碼組?
7.5 如何改變代碼組的權(quán)限集?
7.6 能否創(chuàng)建自己的權(quán)限集?
7.7 CAS 有問題時,如何診斷自己的程序?
7.8 我受不了 CAS 帶來的麻煩,能否關(guān)掉它?
8. 中間語言 (IL)
8.1 我能看到元件的中間語言嗎?
8.2 能否通過反向工程從 IL 中獲得源代碼?
8.3 如何防止別人通過反向工程獲得我的代碼?
8.4 我能直接用 IL 編程嗎?
8.5 IL 能做到 C# 中做不到的事嗎?
9. 關(guān)于 COM
9.1 COM 消亡了嗎?
9.2 DCOM 消亡了嗎?
9.3 MTS/COM+ 消亡了嗎?
9.4 能在 .NET 中使用 COM 組件嗎?
9.5 能在 COM 中使用 .NET 組件嗎?
9.6 在 .NET 的世界中 ATL 是多余的嗎?
10. 雜項
10.1 .NET 的遠(yuǎn)程計算如何工作?
10.2 如何在 .NET 程序中獲得 Win32 API?
11. 類庫
11.1 文件 I/O
11.1.1 如何讀文本文件?
11.1.2 如何寫文本文件?
11.1.3 如何讀寫二進(jìn)制文件?
11.1.4 如何刪除文件?
11.2 文本處理
11.2.1 是否支持正規(guī)表達(dá)式?
11.3 Internet
11.3.1 如何下載網(wǎng)頁?
11.3.2 如何使用代理服務(wù)器 (proxy)?
11.4 XML
11.4.1 是否支持 DOM?
11.4.2 是否支持 SAX?
11.4.3 是否支持 XPath?
11.5 線程
11.5.1 是否支持多線程?
11.5.2 如何產(chǎn)生一個線程?
11.5.3 如何停止一個線程?
11.5.4 怎樣使用線程池?
11.5.5 怎樣知道我的線程池工作項目是在何時完成的?
11.5.6 怎樣防止對數(shù)據(jù)的并發(fā)訪問?
11.6 跟蹤
11.6.1 有內(nèi)置的跟蹤/日志支持嗎?
11.6.2 能否將跟蹤輸出重定向到一個文件?
11.6.3 能否定制跟蹤的輸出?
12. 資源
12.1 從哪里可以獲得關(guān)于 .NET 的詳情?
12.2 示例代碼和實(shí)用程序
1. 簡介
1.1 什么是 .NET?
很難用一句話來講清楚。根據(jù) Microsoft 的說法,.NET 是一個“革命性的新平臺,建立在開放的 Internet 協(xié)議和標(biāo)準(zhǔn)之上,通過工具和服務(wù)將計算和通訊以嶄新的方式融合到一起” 。
更為實(shí)際的定義是:.NET 是一個開發(fā)和運(yùn)行軟件的新環(huán)境,便于開發(fā)基于 Web 的服務(wù),擁有豐富的運(yùn)行庫服務(wù)以支持用多種編程語言編寫的組件,具有跨語言和跨平臺的互操作能力。
注意,本文中使用術(shù)語“.NET”時,它僅指新的 .NET 運(yùn)行庫及其相關(guān)技術(shù)。有時我們也稱其為“.NET 框架”。本文不包括其它 Microsoft 正在往上添加 .NET 名字的任何現(xiàn)有的產(chǎn)品和技術(shù) (例如 SQL Server.NET)。
1.2 .NET 只是 Windows DNA 的一個新名字嗎?
不。在很多地方,Windows DNA 僅僅是指使用現(xiàn)有技術(shù)的一種途徑(即所謂的三階式途徑)的市場術(shù)語。.NET 更為急進(jìn),并且包括一個完整的軟件開發(fā)和運(yùn)行庫框架。
1.3 .NET 只適用于建立 Web 網(wǎng)站嗎?
不。如果你編寫任何 Windows 軟件 (使用 ATL/COM、MFC、VB 甚至 Win32 裸接口),.NET 都可能為你正在做的事情提供可行的選擇 (或補(bǔ)充)。當(dāng)然,如果你就是在開發(fā) Web 網(wǎng)站,.NET 有很多令你感興趣的東西—不僅僅是 ASP+。
1.4 .NET 是在什么時候宣布的?
在 2000 年 6 月 22 日舉行的 Forum 2000 論壇上,Bill Gates 做了一次 演說,勾畫了 .NET 的“前景”。2000 年 7 月的 PDC 會議上針對 .NET 技術(shù)做了很多會談,會談代表得到了包含 .NET Framework/SDK 和 Visual Studio 7 預(yù)覽版的光盤。
1.5 .NET 將在何時發(fā)布?
預(yù)計是在 2001 年的下半年。
1.6 如何開發(fā) .NET 應(yīng)用程序?
.NET Framework SDK 包含可用于建立 .NET 應(yīng)用程序的命令行編譯器和實(shí)用程序。Visual Studio 的下一版本 (稱為 Visual Studio 7 或 Visual Studio.NET) 將完全集成對 .NET 開發(fā)的支持。
1.7 可以從哪里下載 .NET SDK 和 Visual Studio 7?
從 http://msdn.microsoft.com/net 可以下載 SDK 的 Beta 1 版。如果你是 MSDN Universal 訂戶,你還可以下載 Visual Studio 7 的 Beta 1 版。
1.8 .NET 中的關(guān)鍵技術(shù)是什么?
ASP.NET、CLR (Common Language Runtime—通用語言運(yùn)行庫)、C# (新一代的類-Java 語言)、SOAP、XML、ADO.NET、多語言支持 (Eiffel、COBOL 等等)
1.9 .NET 框架將運(yùn)行在什么平臺上?
Beta 1 支持在 Windows 2000、NT4 SP6a, Windows Me 和 Windows 98 上進(jìn)行開發(fā)。Windows 95 支持運(yùn)行庫。
Microsoft 將按照和 .NET 運(yùn)行庫相似的時間表發(fā)布一個新版本 Windows。它的代號是“Whistler”,在很大程度上是對 Windows 2000 的擴(kuò)充性更新,對 GUI 有重要的改變。Microsoft 將以“.NET-enabled”作為新操作系統(tǒng)的賣點(diǎn),但看起來它沒有和 .NET 運(yùn)行庫綁在一起。如果 .NET 運(yùn)行庫能及時完成,它將包含在 Whistler 之內(nèi);否則,Whistler 將單獨(dú)發(fā)貨。
1.10 .NET 框架支持什么語言?
開始 Microsoft 將提供 C#、C++、VB 和 JScript 編譯器。其它供應(yīng)商宣布他們有意開發(fā)像 COBOL、Eiffel、Perl、Smalltalk 和 Python 等語言的 .NET 編譯器。
1.11 .NET 框架符合標(biāo)準(zhǔn)化趨勢嗎?
C# 以及稱為“通用語言基礎(chǔ)結(jié)構(gòu)”的一些東西的推薦標(biāo)準(zhǔn)草案已經(jīng)提交給了 ECMA。參見 http://msdn.microsoft.com/net/ecma/
2. 基本術(shù)語
2.1 什么是 CLR?
CLR = Common Language Runtime—通用語言運(yùn)行庫。CLR 是一組標(biāo)準(zhǔn)資源集合,無論編程語言是什么,所有 (理論上) .NET 程序都能從中獲益。Robert Schmidt (Microsoft) 在他的 MSDN PDC# 文章 中列出了以下 CLR 資源:
面向?qū)ο蟮木幊棠P?(繼承、多態(tài)、異常處理、垃圾收集)
安全模型
類型系統(tǒng)
所有的 .NET 基礎(chǔ)類
許多 .NET 框架類
開發(fā)、調(diào)試和測評工具
運(yùn)行和代碼管理
IL-機(jī)器語言 轉(zhuǎn)換器和優(yōu)化器
這些的含義是,在 .NET 世界里,不同的編程語言將在能力上比過去任何時候都更平等,雖然顯然不是所有語言都支持所有 CLR 服務(wù)。
2.2 什么是 CTS?
CTS = Common Type System—通用類型系統(tǒng)。它是指 .NET 運(yùn)行庫所理解、并且隨后 .NET 應(yīng)用程序可以使用的一系列類型。然而,注意不是所有的 .NET 語言都將支持 CTS 中的所有類型。CTS 是 CLS 的超集。
2.3 什么是 CLS?
CLS = Common Language Specification—通用語言規(guī)范。它是預(yù)計所有 .NET 語言都支持的一個 CTS 的子集。這一思想是讓使用 CLS-相容類型的任何程序和以任何語言編寫的 .NET 程序可以互相操作。
理論上它能允許在不同的 .NET 語言之間有緊密的互操作性—例如允許從一個 VB 類里繼承一個 C# 類。
2.4 什么是 IL?
IL = Intermediate Language—中間語言。又稱為 MSIL。所有 .NET 源代碼 (使用任何語言) 被編譯為 IL。然后在軟件的安裝點(diǎn)上或者運(yùn)行時,IL 由即時 (JIT) 編譯器轉(zhuǎn)換為機(jī)器碼。
2.5 什么是 C#?
C# 是在 .NET 框架中運(yùn)行的一種新語言。在他們的“C# 簡介”白皮書中,Microsoft 這樣描述 C#:
“C# 是從 C 和 C++ 派生出來的一種簡單的、面向?qū)ο蟮、并且是類型安全的現(xiàn)代編程語言。C# (發(fā)音為‘C sharp’) 牢固地根植于在 C 和 C++ 家族之樹,將很快為 C 和 C++ 程序員所熟悉。C# 幫助開發(fā)者將 Visual Basic 的高生產(chǎn)率和 C++ 的直接控制能力結(jié)合起來。”
將以上引言中的“C#”換成“Java”,你會發(fā)現(xiàn)這句陳述依然很正確 :)。
假如你是一位 C++ 程序員,你可能想看看我的 C# FAQ。
2.6 在 .NET 范疇里,“被管理”是什么含義?
術(shù)語“被管理”導(dǎo)致了很多誤解。在 .NET 里的不同地方都使用了它,分別指相互差別不大的不同東西。
被管理代碼:.NET 框架為運(yùn)行在其上的程序提供了幾個核心的運(yùn)行服務(wù)—例如異常處理和安全性。為使這些服務(wù)能工作,代碼必須提供運(yùn)行時的最低程度的一些信息。這樣的代碼被稱為被管理代碼。默認(rèn)情況下,所有 C#、Visual Basic.NET 和 JScript.NET 代碼都是被管理代碼。如不指明,VS7 C++ 代碼不是被管理代碼,但能通過一個命令行開關(guān) (/com+) 使編譯器產(chǎn)生被管理代碼。
被管理數(shù)據(jù):是指由 .NET 運(yùn)行庫的垃圾收集器分配和回收的數(shù)據(jù)。C#、VB.NET 和 JScript.NET 數(shù)據(jù)總是被管理的。即使使用了 /com+ 開關(guān),默認(rèn)情況下 VS7 C++ 數(shù)據(jù)也不是被管理的,但可以使用 __gc 關(guān)鍵字將其指定為被管理數(shù)據(jù)。
被管理類:通常在 C++ 的 Managed Extensions (ME) 范疇中涉及。使用 ME C++ 時,可以用 __gc 關(guān)鍵字將其指定為被管理的。名副其實(shí),該類的實(shí)例所占用的內(nèi)存由垃圾收集器管理,但還不止如此。該類還成為了完全的 .NET 團(tuán)體的成員,同時帶來了好處和限制。好處之一是獲得了與其它語言編寫的類之間的互操作性—例如,一個被管理 C++ 類可以繼承 VB 類。限制之一是被管理類只能繼承一個基類。
2.7 什么是映像?
所有的 .NET 編譯器都產(chǎn)生關(guān)于它們所產(chǎn)生的模塊中的類型定義的特殊數(shù)據(jù)。這些特殊數(shù)據(jù)同模塊封裝在一起 (隨后模塊被封裝到元件中),可以通過稱為映像 的機(jī)制來訪問。System.Reflection 命名空間中包含向模塊或元件詢問其類型的類。
使用映像來訪問 .NET 的特殊數(shù)據(jù)同使用 ITypeLib/ITypeInfo 來訪問 COM 中的類型庫數(shù)據(jù)非常相似,而且使用的目的也很相似—例如確定數(shù)據(jù)類型大小,以便在上下文、進(jìn)程、機(jī)器的邊界間調(diào)度它們。
映像還可以被用來動態(tài)調(diào)用方法 (參見 System.Type.InvokeMember),甚至在運(yùn)行時動態(tài)創(chuàng)建類型 (參見 System.Reflection.Emit.TypeBuilder )。
3. 元件
3.1 什么是元件?
元件有時被描述為一個邏輯上的 .EXE 或 .DLL,它可以是任何一個應(yīng)用程序 (有一個主入口點(diǎn)) 或庫。一個元件由一個或多個文件組成 (dll、exe、html 文件等等),表示一組資源、類型定義以及這些類型的實(shí)現(xiàn)。一個元件也可以包含對其它元件的引用。這些資源、類型和引用在稱為清單的一個數(shù)據(jù)塊中描述。清單是元件的一部分,這樣一來元件就是自描述的。
元件的一個重要方面使他們是一個類型的唯一標(biāo)志的一部分。類型的唯一標(biāo)志是將它所在的元件和類型名組合在一起得到的。這就是說,例如,如果元件 A 輸出了一個稱為 T 的類型,同時元件 B 輸出了一個也稱為 T 的類型,.NET 運(yùn)行庫將它們視為完全不同的兩個類型。此外,不要混淆元件和命名空間—命名空間僅僅是組織類型名字的一種層次化方法。對于運(yùn)行庫,不論使用哪一個命名空間來組織名字,類型名就是類型名。從運(yùn)行庫來看,是元件加上類型名 (無論類型名屬于哪個命名空間) 唯一地標(biāo)識出一個類型。
元件在 .NET 的安全方面也很重要—許多安全限制是在元件的邊界上實(shí)施的。
最后,元件是 .NET 中版本控制的單元—詳情見下文。
3.2 怎樣創(chuàng)建元件?
創(chuàng)建元件最簡單的方法是直接使用 .NET 編譯器。例如,以下 C# 程序:
public class CTest
{
public CTest()
{
System.Console.WriteLine( "Hello from CTest" );
}
}
能用以下方法編譯為一個庫元件 (dll):
csc /t:library ctest.cs
通過運(yùn)行 .NET SDK 所帶的“IL 反匯編”工具,你能看到元件的內(nèi)容。
另外你也能把你的源代碼編譯成模塊,然后使用元件連接器 (al.exe) 將模塊組合成一個元件。對 C# 編譯器,/target:module 開關(guān)可以指定產(chǎn)生模塊而不是元件。
3.3 私有元件和共享元件有什么不同?
空間分配和可見性:私有元件通常由一個應(yīng)用程序使用,被存儲到這個應(yīng)用程序的目錄或其下的子目錄之下。共享元件通常存儲到全局的元件緩沖區(qū)中,這里是 .NET 運(yùn)行庫維護(hù)的元件的儲藏所。共享元件通常是許多應(yīng)用程序都要用到的代碼庫,例如 .NET 框架類。
版本控制:運(yùn)行庫只對共享元件實(shí)施版本約束,而不對私有元件實(shí)施。
3.4 元件如何相互找到?
通過尋找目錄路徑。有幾個因素會影響路徑 (比如 AppDomain 宿主、應(yīng)用程序配置文件等),但對于私有元件,搜索路徑通常是應(yīng)用程序的目錄及其子目錄。對于共享元件,搜索路徑通常和私有元件的一樣,再加上共享元件緩沖區(qū)。
3.5 元件版本如何起作用?
每個元件由一個稱為兼容性版本的版本號。同樣,對元件的引用 (從另一個元件) 包括被引用元件的名稱和版本。
版本號有四個數(shù)字部分 (例如 5.5.2.33)。前兩部分不相同的元件被視為不兼容的。如果前兩部分相同,但第三部分不同,元件被認(rèn)為“可能兼容”。如果僅僅第四部分不同,則元件被視為是兼容的。然而,這只是默認(rèn)的指導(dǎo)方針—是 版本策略決定施用這些規(guī)則的范圍。版本策略可以在應(yīng)用程序配置文件中指定。
記。喊姹究刂苾H僅針對于共享元件,而不對私有元件。
4. 應(yīng)用程序域
4.1 什么是應(yīng)用程序域?
應(yīng)用程序域 (AppDomain) 可以被看作一個輕型的進(jìn)程。在一個 Win32 進(jìn)程中可以存在多個 AppDomain。AppDomain 的主要目的是將應(yīng)用程序和其它應(yīng)用程序隔離開來。
通過使用獨(dú)立的地址空間,Win32 進(jìn)程提供隔離性。這種方法很有效,但開銷很大并且伸縮性不好。.NET 運(yùn)行庫通過控制對內(nèi)存的是用來施加 AppDomain 隔離—AppDomain 中的所有內(nèi)存是由 .NET 運(yùn)行庫來管理的,所以運(yùn)行庫可以確保 AppDomain 之間不能訪問彼此的內(nèi)存。
4.2 如何創(chuàng)建 AppDomain?
AppDomains 通常有宿主創(chuàng)建。宿主包括 Windows Shell、ASP+ 和 IE。當(dāng)你從命令行運(yùn)行一個 .NET 應(yīng)用程序時,宿主是 Shell。Shell 為每個應(yīng)用程序創(chuàng)建一個新的 AppDomain。
AppDomains 也可以由 .NET 應(yīng)用程序來顯式創(chuàng)建。這里是一個創(chuàng)建 AppDomain 的一個 C# 例子,它創(chuàng)建對象的一個實(shí)例,并隨后執(zhí)行對象的一個方法:
using System;
using System.Runtime.Remoting;
public class CAppDomainInfo : MarshalByRefObject
{
public string GetAppDomainInfo()
{
return "AppDomain = " + AppDomain.CurrentDomain.FriendlyName;
}
}
public class App
{
public static int Main()
{
AppDomain ad = AppDomain.CreateDomain( "Andy's new domain", null, null );
ObjectHandle oh = ad.CreateInstance( "appdomaintest.exe", "CAppDomainInfo" );
CAppDomainInfo adInfo = (CAppDomainInfo)(oh.Unwrap());
string info = adInfo.GetAppDomainInfo();
Console.WriteLine( "AppDomain info: " + info );
return 0;
}
}
4.3 我能編寫自己的 .NET 宿主嗎?
能。關(guān)于怎樣來做的例子,看看 Jason Whittington 和 Don Box 開發(fā)的 dm.net moniker 的源代碼 (http://staff.develop.com/jasonw/clr/readme.htm)。在 .NET SDK 中也有一個叫作 CorHost 的代碼示例。
5. 垃圾收集
5.1 什么是垃圾收集?
垃圾收集是一個系統(tǒng),運(yùn)行庫組件通過它來管理對象的生存周期和它們占用的堆內(nèi)存。對 .NET 而言它并不是一個新概念—Java 和許多其它語言/運(yùn)行庫使用垃圾收集已經(jīng)有一段時間了。
5.2 對對象的最后一個引用撤銷后,它并不一定立即被破壞,對嗎?
是的。垃圾收集器并不提供銷毀對象并是放其內(nèi)存的時間保證。
關(guān)于 C# 中隱含的非確定化對象析構(gòu),Chris Sells 有一個令人感興趣的線索:http://discuss.develop.com/archives/wa.exe?A2=ind0007&L=DOTNET&P=R24819
2000 年 10 月,Microsoft 的 Brian Harry 貼出了一個針對這個問題的很長的分析:http://discuss.develop.com/archives/wa.exe?A2=ind0010A&L=DOTNET&P=R28572
Chris Sells 對 Brian 貼子的答復(fù)在這里:http://discuss.develop.com/archives/wa.exe?A2=ind0010C&L=DOTNET&P=R983
5.3 .NET 為什么不提供確定化的析構(gòu)?
因?yàn)槔占惴ā?NET 的垃圾收集器通過周期地掃描應(yīng)用程序正在使用的所有對象的列表來工作。掃描過程中所有未被發(fā)現(xiàn)的對象就可以被銷毀并釋放內(nèi)存。當(dāng)對對象的最后一個引用撤銷后,算法的這種實(shí)現(xiàn)使運(yùn)行庫不能立即得到通知—它只能在下一次清理堆時發(fā)現(xiàn)。
而且,這種算法盡可能少地進(jìn)行垃圾收集,以便工作得最有效率。通常,堆容量的消耗會觸發(fā)收集過程。
5.4 在 .NET 中缺少確定化的析構(gòu)有問題嗎?
這確實(shí)會影響組件的設(shè)計。如果你的對象需要昂貴或緊缺的資源 (例如對數(shù)據(jù)庫的鎖定),你需要提供某種方法讓客戶端在工作完成后能告訴對象以釋放資源。Microsoft 建議,為此目的你應(yīng)提供一個稱為 Dispose () 的方法。然而,這樣會在分布式對象中引起問題—在一個分布式系統(tǒng)中由誰來調(diào)用 Dispose () 方法?需要有某種形式的引用-計數(shù)機(jī)制或所有者管理機(jī)制來處理分布式對象—不幸的是運(yùn)行庫對此愛莫能助。
5.5 確定化的析構(gòu)是否影響在被管理代碼中使用 COM 對象?
是的。從被管理代碼中使用 COM 對象時,你實(shí)際上是依賴?yán)占鱽碜罱K釋放你的對象。如果你的 COM 對象占有昂貴的資源且只能在最終釋放對象后才能釋放,你可能需要在你的對象上提供一個新接口以支持顯式的 Dispose () 方法。
5.6 我聽說應(yīng)該避免使用 Finalize 方法,那么是否應(yīng)該在我的類理實(shí)現(xiàn) Finalize?
對垃圾收集器而言,擁有 Finalize 方法的對象比沒有此方法的對象需要做更多的工作。同時也不保證對象 Finalized 的次序,所以對于從 Finalized 方法訪問其它對象有不同的看法。最后,不能保證 Finalized 方法一定能被調(diào)用。所以,永遠(yuǎn)不應(yīng)該依賴它來清理對象的資源。
Microsoft 建議使用以下方式:
public class CTest
{
public override void Dispose()
{
... // Cleanup activities
GC.SuppressFinalize(this);
}
protected override void Finalize()
{
Dispose();
}
}
一般情況下客戶端調(diào)用 Dispose (),對象的資源被釋放,并且通過調(diào)用 SuppressFinalize (),垃圾收集器被免除了對它進(jìn)行 Finalize 的義務(wù)。在最不利的情況下,即客戶端忘記了調(diào)用 Dispose (),有很大的機(jī)會通過垃圾收集器調(diào)用 Finalize () 來最終釋放對象的資源。由于垃圾收集算法的缺陷,這看起來像是相當(dāng)合理的處理辦法了。
5.7 我有控制垃圾收集算法的手段嗎?
有一點(diǎn)。System.GC 類提供了一對有趣的方法。第一個是 Collect 方法—它強(qiáng)制垃圾收集器立即收集所有未被引用的對象。另一個是 RequestFinalizeOnShutdown (),它告訴垃圾收集器在應(yīng)用程序關(guān)閉時一定要對每個對象運(yùn)行 Finalize () 方法。在應(yīng)用程序關(guān)閉時,垃圾收集器一般優(yōu)先選擇快速的推出方式而不是調(diào)用 Finzlize (),所以這個方法能手工強(qiáng)制運(yùn)行庫多負(fù)一點(diǎn)責(zé)任。
如果你想驗(yàn)證這不僅僅是理論上的說法,是一十下面的測試程序:
using System;
class CTest
{
protected override void Finalize()
{
Console.WriteLine( "This is the Finalizer." );
}
}
class CApplication
{
public static void Main()
{
Console.WriteLine( "This is Main." );
CTest test = new CTest();
// GC.RequestFinalizeOnShutdown();
}
}
運(yùn)行此程序,然后再去掉 GC.RequestFinalizeOnShutdown() 這一行前面的注釋標(biāo)記并重新運(yùn)行,注意有什么不同……
5.8 我怎么知道垃圾收集器在做什么?
.NET 運(yùn)行庫中很多令人感興趣的統(tǒng)計通過 'COM+ Memory' 性能對象輸出。使用 Performance Monitor 查看它們。
6. 屬性
6.1 什么是屬性?
最少有兩種類型的 .NET 屬性。第一類我稱其為 metadata 屬性—它允許將某些數(shù)據(jù)附加到類或方法上。這些數(shù)據(jù)稱為類的 metadata 的一部分,并且可以像類的其它 metadata 一樣通過映射來訪問。metadata 的另一種屬性是 [serializable],將它附加到類上表示類的實(shí)例可以被串行化。
[serializable] public class CTest {}
另一種類型的屬性是上下文屬性。上下文類型的屬性使用和 metadata 相似的語法,但實(shí)際上它們是不同的。上下文類型屬性提供一種解釋機(jī)制,通過這種機(jī)制,實(shí)例的活動和方法調(diào)用可以是預(yù)先處理和/或隨后處理的。如果你了解 Keith Brown 的通用委托器你可能熟悉這種思想。
6.2 我能創(chuàng)建自己的 metadata 屬性嗎?
是的。簡單地從 System.Attribute 導(dǎo)出一個類并將其標(biāo)記為 AttributeUsage 屬性。例如:
[AttributeUsage(AttributeTargets.Class)]
public class InspiredByAttribute : System.Attribute
{
public string InspiredBy;
public InspiredByAttribute( string inspiredBy )
{
InspiredBy = inspiredBy;
}
}
[InspiredBy("Andy Mc's brilliant .NET FAQ")]
class CTest
{
}
class CApp
{
public static void Main()
{
object[] atts = typeof(CTest).GetCustomAttributes();
foreach( object att in atts )
if( att is InspiredByAttribute )
Console.WriteLine( "Class CTest was inspired by {0}", _
((InspiredByAttribute)att).InspiredBy );
}
}
6.3 我能創(chuàng)建自己的 context 屬性嗎?
是的。看看 http://www.develop.com/dbox/dotnet/threshold/ 處的 Don Box 的例子 (叫作 CallThreshold) 和 http://www.razorsoft.net/ 處的 Perter Drayton 的 Tracehook.NET
7. 代碼訪問安全性
7.1 什么是代碼訪問安全性 (CAS)?
CAS 是 .NET 安全性模型的一部分,它確定一段代碼是否允許被運(yùn)行,以及當(dāng)它運(yùn)行是可以使用什么資源。例如,CAS 可以防止一個 .NET 的 Web applet 將你的硬盤格式化。
7.2 CAS 如何起作用?
CAS 安全策略設(shè)計兩個關(guān)鍵概念—代碼組和權(quán)限。每個 .NET 元件是特定 代碼組的成員,并且每個代碼組被授予由有名權(quán)限集所指定的權(quán)限。
例如,使用默認(rèn)的安全策略時,一個從 Web 站點(diǎn)下載的控件屬于“Zone - Internet”代碼組,它保持由有名權(quán)限集“Internet”所定義的權(quán)限。(自然,有名權(quán)限集“Internet”表示一組受到嚴(yán)格限制的權(quán)限。)
7.3 誰定義 CAS 代碼組?
Microsoft 定義了一些默認(rèn)代碼組,但你可以改變這些甚至創(chuàng)建你自己的代碼組。要想看到你的系統(tǒng)中定義的代碼組,可以從命令橫行運(yùn)行“caspol -lg”命令。再我的系統(tǒng)里它看起來像這些:
Level = Machine
Code Groups:
1. All code: Nothing
1.1. Zone - MyComputer: FullTrust
1.1.1. Honor SkipVerification requests: SkipVerification
1.2. Zone - Intranet: LocalIntranet
1.3. Zone - Internet: Internet
1.4. Zone - Untrusted: Nothing
1.5. Zone - Trusted: Internet
1.6. StrongName - 0024000004800000940000000602000000240000525341310004000003
000000CFCB3291AA715FE99D40D49040336F9056D7886FED46775BC7BB5430BA4444FEF8348EBD06
F962F39776AE4DC3B7B04A7FE6F49F25F740423EBF2C0B89698D8D08AC48D69CED0FC8F83B465E08
07AC11EC1DCC7D054E807A43336DDE408A5393A48556123272CEEEE72F1660B71927D38561AABF5C
AC1DF1734633C602F8F2D5: Everything
注意代碼組的層次—頂層 ('All code') 是最通用的,它隨后分為幾個組,每個還可以再分。同時注意,和一般的想象不同,子組可以被賦予比它的上級更寬的權(quán)限集。
7.4 如何定義自己的代碼組?
使用 caspol。例如,假定你信任來自 www.mydomain.com 的代碼,并且希望它對你的系統(tǒng)擁有完全的訪問權(quán),但是希望對其它 Internet 站點(diǎn)保持默認(rèn)的限制。要實(shí)現(xiàn)這些,你可以在“Zone - Internet”組中增加一個子組,就像下面那樣:
caspol -ag 1.3 -site www.mydomain.com FullTrust
現(xiàn)在如果你運(yùn)行 caspol -lg 就可以看到新的代碼組被增加為 1.3.1 組:
...
1.3. Zone - Internet: Internet
1.3.1. Site - www.mydomain.com: FullTrust
...
注意數(shù)字標(biāo)號 (1.3.1) 只是 caspol 編出來以便能從命令行方便地操縱代碼組的。底層的運(yùn)行庫永遠(yuǎn)看不到它。
7.5 如何改變代碼組的權(quán)限集?
使用 caspol。如果你是機(jī)器的管理員,你能在 'machine' 層次上操作—這不僅意味著你所做的改變將成為機(jī)器的默認(rèn)設(shè)置,而且用戶不能把權(quán)限改得更寬。如果你是一個普通用戶 (不是管理員) 你仍然可以修改權(quán)限,但只能使它們變得更嚴(yán)格。例如,為使 intranet 代碼能做它們想做的事,你可能需要這樣:
caspol -cg 1.2 FullTrust
注意,因?yàn)?(在標(biāo)準(zhǔn)的系統(tǒng)里) 這比默認(rèn)的安全策略權(quán)限更大,你應(yīng)該在 machine 層次上做這些—在 user 層次上這樣做不起作用。
7.6 能否創(chuàng)建自己的權(quán)限集?
是的。使用 caspol -ap,指定一個包含權(quán)限集中所有的權(quán)限的 XML 文件。這里 是一個指定 'Everything' 權(quán)限集的示例文件—修改它以適應(yīng)你的需要,這樣可以節(jié)省一些時間。修改完成后,用以下方法將它添加到可用的權(quán)限集中:
caspol -ap samplepermset.xml
然后,用以下方法將此權(quán)限集施加到一個代碼組上:
caspol -cg 1.3 SamplePermSet
(默認(rèn)情況下,1.3 是 'Internet' 代碼組)
7.7 CAS 有問題時,如何診斷自己的程序?
caspol 有一組可能有用的選項。首先,使用 caspol -rsg,你能讓 caspol 告訴你一個元件屬于哪一個代碼組。類似地,使用 caspol -rsp,你能詢問在特定元件上施加了什么權(quán)限。
7.8 我受不了 CAS 帶來的麻煩,能否關(guān)掉它?
是的,只要你是系統(tǒng)管理員。只要運(yùn)行:
caspol -s off
8. 中間語言 (IL)
8.1 我能看到元件的中間語言嗎?
是的。Microsoft 提供了一個稱為 Ildasm 的工具,它可以用來查看元件的 metadata 和 IL。
8.2 能否通過反向工程從 IL 中獲得源代碼?
是的。相對而言,從 IL 來重新生成高級語言源代碼 (例如 C#) 通常是很簡單的。
8.3 如何防止別人通過反向工程獲得我的代碼?
目前唯一的辦法是運(yùn)行帶有 /owner 選項的 ilasm。這樣生成的元件的 IL 不能通過 ildasm 來查看。然而,意志堅定的代碼破譯者能夠破解 ildasm 或者編寫自己的 ildasm 版本,所以這種方法只能嚇唬那些業(yè)余的破譯者。
不幸的事,目前的 .NET 編譯器沒有 /owner 選項,所以要想保護(hù)你的 C# 或 VB.NET 元件,你需要像下面那樣做:
csc helloworld.cs
ildasm /out=temp.il helloworld.exe
ilasm /owner temp.il
(這個建議是 Hany Ramadan 貼到 DOTNET 上的。)
看起來過一段時間能有 IL 加密工具 (無論來自 Microsoft 或第三方)。這些工具會以這樣的方式來“優(yōu)化” IL:使反向工程變得更困難。
當(dāng)然,如果你是在編寫 Web 服務(wù),反向工程看起來就不再是一個問題,因?yàn)榭蛻舨荒茉L問你的 IL。
8.4 我能直接用 IL 編程嗎?
是的。Peter Drayton 在 DOTNET 郵件列表里貼出了這個簡單的例子:
.assembly MyAssembly {}
.class MyApp {
.method static void Main() {
.entrypoint
ldstr "Hello, IL!"
call void System.Console::WriteLine(class System.Object)
ret
}
}
將其放入名為 hello.il 的文件中,然后運(yùn)行 ilasm hello.il,將產(chǎn)生一個 exe 元件。
8.5 IL 能做到 C# 中做不到的事嗎?
是的。一些簡單的例子是:你能拋出不是從 SystemException 導(dǎo)出的異常,另外你能使用非以零起始的數(shù)組。
9. 關(guān)于 COM
9.1 COM 消亡了嗎?
就像你在郵件列表中看到的那樣,這個主題導(dǎo)致了激烈的爭論。看看以下兩個地方:
http://discuss.develop.com/archives/wa.exe?A2=ind0007&L=DOTNET&D=0&P=68241
http://discuss.develop.com/archives/wa.exe?A2=ind0007&L=DOTNET&P=R60761
我的理解是:COM 包含很多內(nèi)容,并且對于不同的人而言它是不同的東西。但是對我來說,COM 基本上是關(guān)于一小段代碼如何找到另一小段代碼,以及當(dāng)它們相互找到后該如何相互通訊。COM 準(zhǔn)確地指明了這種定位和通訊該如何進(jìn)行。在完全由 .NET 對象構(gòu)成的“純” .NET 世界里,小段代碼依然相互尋找并相互交談,但它們不使用 COM 來做這些。它們使用在某些地方和 COM 很相像的一種模型—例如,類型信息保存在和組件封裝在一起的表單中,這和在 COM 組件中封裝一個類型庫十分相似。但它不是 COM。
所以,這里有什么問題嗎?好吧,我確實(shí)不關(guān)心大多數(shù) COM 消失了—我不關(guān)心尋找組件不再和注冊表有關(guān),我也不使用 IDL 來定義我的借口。但有一件東西我不希望它消失—我不希望失去基于接口的開發(fā)這種思想。照我看來,COM 最強(qiáng)大的力量是它堅持在接口和實(shí)現(xiàn)之間豎起鑄鐵般的隔墻。不幸的是,看來 .NET 不再那樣堅持—它允許你做基于接口的開發(fā),但它并不堅持。一些人可能會辯解說有一個選擇總不會是壞事,可能他們是對的,但我不能不覺得這可能是一個退步。
9.2 DCOM 消亡了嗎?
差不多是,尤其是對于 .NET 開發(fā)者。.NET 框架有一個不基于 DCOM 的新的遠(yuǎn)程模型。當(dāng)然 DCOM 還會在互操作場合下使用。
9.3 MTS/COM+ 消亡了嗎?
不。第一個 .NET 版本考慮的是提供對現(xiàn)有 COM+ 服務(wù) (通過一個互操作層) 而不是使用 .NET 自己的服務(wù)來取代它們。很多工具和屬性被用以實(shí)現(xiàn)盡可能平滑的過渡。.NET SDK 的 PDC 版本包括對核心服務(wù) (JIT 活動、事務(wù)) 的支持,但不包括一些高層服務(wù) (例如 COM+ 事件、隊列化組件)。
在一段時間內(nèi)看來,互操作性可以預(yù)期是無縫集成的—這意味著一些服務(wù)將成為 CLR 的一部分,并且/或者意味著一些服務(wù)將以可管理代碼的形式重寫并運(yùn)行在 CLR 的頂層。
關(guān)于這個主題,參見 Joe Long 的貼子—Joe 是 Microsoft 的 COM+ 組的經(jīng)理。從這里開始:
http://discuss.develop.com/archives/wa.exe?A2=ind0007&L=DOTNET&P=R68370
9.4 能在 .NET 中使用 COM 組件嗎?
可以?梢酝ㄟ^ Runtime Callable Wrapper (RCW) 從 .NET 中訪問 COM 組件。它通過將 COM 組件映射為與 .NET 兼容的接口來使 COM 接口可以被訪問。對于 oldautomation 接口,可以自動地從一個類型庫中產(chǎn)生。對于非 oleautomation 接口,可以開發(fā)一個定制的 RCW,以便手工地將 COM 接口的類型映射為與 .NET 兼容的類型。
對于熟悉 ATL 的讀者,這里有一個簡單的示例。首先,創(chuàng)建一個 ATL 組件以實(shí)現(xiàn)以下 IDL:
import "oaidl.idl";
import "ocidl.idl";
[
object,
uuid(EA013F93-487A-4403-86EC-FD9FEE5E6206),
helpstring("ICppName Interface"),
pointer_default(unique),
oleautomation
]
interface ICppName : IUnknown
{
[helpstring("method SetName")] HRESULT SetName([in] BSTR name);
[helpstring("method GetName")] HRESULT GetName([out,retval] BSTR *pName );
};
[
uuid(F5E4C61D-D93A-4295-A4B4-2453D4A4484D),
version(1.0),
helpstring("cppcomserver 1.0 Type Library")
]
library CPPCOMSERVERLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");
[
uuid(600CE6D9-5ED7-4B4D-BB49-E8D5D5096F70),
helpstring("CppName Class")
]
coclass CppName
{
[default] interface ICppName;
};
};
建立了組件以后,你會得到一個 typelibrary。在 typelibrary 上運(yùn)行 TLBIMP 實(shí)用程序,就像這樣:
tlbimp cppcomserver.tlb
如果成功,你會得到像這樣的信息:
Typelib imported successfully to CPPCOMSERVERLib.dll
現(xiàn)在你需要一個 .NET 客戶端—我們用 C# 創(chuàng)建一個包含以下代碼的 .cs 文件:
using System;
using CPPCOMSERVERLib;
public class MainApp
{
static public void Main()
{
CppName cppname = new CppName();
cppname.SetName( "bob" );
Console.WriteLine( "Name is " + cppname.GetName() );
}
}
注意我們使用 typelibrary 的名字作為命名空間,COM 類的名字作為類名。我們也可以選擇使用 CPPCOMSERVERLib.CppName 作為類名而且不需要語句 using CPPCOMSERVERLib。
像這樣編譯以上 C# 代碼:
csc /r:cppcomserverlib.dll csharpcomclient.cs
注意,編譯被告知,引用我們剛才用 TLBIMP 從 typelibrary 產(chǎn)生的 DLL。
現(xiàn)在你應(yīng)該可以運(yùn)行 csharpcomclient.exe,并從控制臺得到如下輸出:
Name is bob
9.5 能在 COM 中使用 .NET 組件嗎?
可以?梢酝ㄟ^一個 COM Callable Wraper (CCW) 從 COM 中訪問 .NET 組件。這和 RCW 很相似 (參見上一個問題),但以相反的方向工作。同樣,如果它不能由 .NET 開發(fā)工具自動產(chǎn)生,或不想要自動產(chǎn)生的行為邏輯,可以開發(fā)一個定制的 CCW。為使 COM 可以“看見” .NET 組件,.NET 組件必須在注冊表里注冊。
這里是一個簡單的例子。創(chuàng)建一個名為 testcomserver.cs 的 C# 文件并輸入下面的代碼:
using System;
namespace AndyMc
{
public class CSharpCOMServer
{
public CSharpCOMServer() {}
public void SetName( string name ) { m_name = name; }
public string GetName() { return m_name; }
private string m_name;
}
}
然后編譯 .cs 文件:
csc /target:library testcomserver.cs
你會得到一個 dll,這樣將它注冊:
regasm testcomserver.dll /tlb:testcomserver.tlb
現(xiàn)在你需要創(chuàng)建一個客戶端程序來測試你的 .NET COM 組件。VBScript 可以—將以下內(nèi)容放到一個名為 comclient.vbs 的文件中:
Dim dotNetObj
Set dotNetObj = CreateObject("AndyMc.CSharpCOMServer")
dotNetObj.SetName ("bob")
MsgBox "Name is " & dotNetObj.GetName()
運(yùn)行此腳本:
wscript comclient.vbs
嘿!你得到一個顯示文本“Name is bob”的消息框。
(注意,編寫此程序時,看起來可以通過幾種路徑將 .NET 類作為 COM 組件訪問—為了避免問題,在 testcomserver.dll 相同的目錄下運(yùn)行 comclient.vbs。
一種替代的方法是使用 Jason Whittington 和 Don Box 開發(fā)的 dm.net moniker。到這里 http://staff.develop.com/jasonw/clr/readme.htm 查看。
9.6 在 .NET 的世界中 ATL 是多余的嗎?
是的。如果你在編寫 .NET 框架內(nèi)的應(yīng)用程序。當(dāng)然許多開發(fā)者希望繼續(xù)使用 ATL 來編寫 .NET 框架以外的 C++ COM 組件,但當(dāng)你在 .NET 框架內(nèi)時你差不多總是希望使用 C#。在 .NET 世界里,原始的 C++ (以及基于它的 ATL) 并沒有太多的地位—它太直接了,并且提供了太多的適應(yīng)性,以至于運(yùn)行庫不能管理它。
10. 雜項
10.1 .NET 的遠(yuǎn)程計算如何工作?
.NET 的遠(yuǎn)程計算涉及通過通道發(fā)送消息。兩種標(biāo)準(zhǔn)的通道是 HTTP 和 TCP。僅僅在局域網(wǎng)上才傾向于使用 TCP—HTTP 能在局域網(wǎng)和廣域網(wǎng) (internet) 上使用。
現(xiàn)在提供了對多種消息串行化格式的支持,例如 SOAP (基于 XML) 和二進(jìn)制格式。默認(rèn)情況下,HTTP 通道使用 SOAP (通過 .NET 運(yùn)行庫的 Serialization SOAP Formatter),而 TCP 通道使用二進(jìn)制格式 (通過 .NET 運(yùn)行庫的 Serialization Binary Formatter)。但每個通道可以使用任一串行化格式。
這里是遠(yuǎn)程訪問的一些方式:
SingleCall。每個來自客戶端的請求由一個新對象服務(wù)。當(dāng)請求完成后對象被丟棄?梢栽 ASP+ 環(huán)境中使用 ASP+ 國家服務(wù)來保存應(yīng)用程序或會話的國家,從而使這種模型 (無國家之分的) 變成有國家支持的。
Singleton。所有來在客戶端的請求由單一的服務(wù)器對象處理。
Client-activated object。這是老的有國家支持的 (D)COM 模型,這里客戶端受到一個遠(yuǎn)端對象的引用并保留此引用 (以保持遠(yuǎn)端對象的生存),直到對它的訪問完成。
對象的分布式垃圾收集由稱為“基于租用的生命周期”管理。每個對象擁有一個租用時間,這個時間到達(dá)時,從 .NET 運(yùn)行庫的遠(yuǎn)程子結(jié)構(gòu)斷開對象。對象有默認(rèn)的更新時間—從客戶端發(fā)起的成功調(diào)用會更新租用時間?蛻舳艘部梢燥@示地更新租用時間。
如果你對使用 XML-RPC 來代替 SOAP,可以看看 Charles Cook 在 http://www.cookcomputing.com/xmlrpc/xmlrpc.shtml 的 XML-RPC.Net 站點(diǎn)。
10.2 如何在 .NET 程序中獲得 Win32 API?
使用 P/Invoke。它使用了和 COM 互操作性相似的技術(shù),但被用來訪問靜態(tài) DLL 入口點(diǎn)而不是 COM 對象。以下是一個調(diào)用 Win32 MessageBox 函數(shù)的 C# 程序示例:
using System;
using System.Runtime.InteropServices;
class MainApp
{
[dllimport("user32.dll", EntryPoint="MessageBox", SetLastError=true, CharSet=CharSet.Auto)]
public static extern int MessageBox(int hWnd, String strMessage, String strCaption, uint uiType);
public static void Main()
{
MessageBox( 0, "Hello, this is PInvoke in operation!", ".NET", 0 );
}
}
11. 類庫
11.1 文件 I/O
11.1.1 如何讀文本文件?
首先,使用 System.IO.FileStream 對象打開文件:
FileStream fs = new FileStream( @"c:\test.txt", FileMode.Open, FileAccess.Read );
FileStream 繼承于 Stream,所以你可以用一個 StreamReader 對象把 FileStream 對象包裝起來。這樣為一行一行地進(jìn)行流處理提供了一個良好的界面:
StreamReader sr = new StreamReader( fs );
string curLine;
while( (curLine = sr.ReadLine()) != null )
Console.WriteLine( curLine );
最后關(guān)閉 StreamReader 對象:
sr.Close();
注意這樣將自動地在底層 Stream 對象上調(diào)用 Close (),所以不必顯示地執(zhí)行 fs.Close()。
11.1.2 如何寫文本文件?
和讀文件的例子相似,只是把 StreamReader 換成 StreamWriter。
11.1.3 如何讀寫二進(jìn)制文件?
和文本文件類似,只是要用 BinaryReader/Writer 對象而不是 StreamReader/Writer 來包裝 FileStream 對象。
11.1.4 如何刪除文件?
在 System.IO.File 對象上使用靜態(tài)方法 Delete ():
File.Delete( @"c:\test.txt" );
11.2 文本處理
11.2.1 是否支持正規(guī)表達(dá)式?
是的。使用 System.Text.RegularExpressions.Regex 類。例如,以下代碼更新 HTML 文件的標(biāo)題:
FileStream fs = new FileStream( "test.htm", FileMode.Open, FileAccess.Read );
StreamReader sr = new StreamReader( fs );
Regex r = new Regex( "<TITLE>(.*)</TITLE>" );
string s;
while( (s = sr.ReadLine()) != null )
{
if( r.IsMatch( s ) )
s = r.Replace( s, "<TITLE>New and improved ${1}</TITLE>" );
Console.WriteLine( s );
}
11.3 Internet
11.3.1 如何下載網(wǎng)頁?
首先使用 System.Net.WebRequestFactory 類來獲得一個 WebRequest 對象:
WebRequest request = WebRequestFactory.Create( "http://localhost" );
然后請求應(yīng)答:
WebResponse response = request.GetResponse();
GetResponse 方法被阻塞直到下載完成。然后你能像下面那樣訪問應(yīng)答流:
Stream s = response.GetResponseStream();
// Output the downloaded stream to the console
StreamReader sr = new StreamReader( s );
string line;
while( (line = sr.ReadLine()) != null )
Console.WriteLine( line );
注意 WebRequest 和 WebReponse 對象分別向下兼容 HttpWebRequest 和 HttpWebReponse 對象,它們被用來訪問和 http 相關(guān)的功能。
11.3.2 如何使用代理服務(wù)器 (proxy)?
兩種—這樣做以便影響所有 Web 請求:
System.Net.GlobalProxySelection.Select = new DefaultControlObject( "proxyname", 80 );
另外一種,要想對特定的 Web 請求設(shè)置代理服務(wù),這樣做:
ProxyData proxyData = new ProxyData();
proxyData.HostName = "proxyname";
proxyData.Port = 80;
proxyData.OverrideSelectProxy = true;
HttpWebRequest request = (HttpWebRequest)WebRequestFactory.Create( "http://localhost" );
request.Proxy = proxyData;
11.4 XML
11.4.1 是否支持 DOM?
是的?纯匆韵率纠 XML文檔:
<PEOPLE>
<PERSON>Fred</PERSON>
<PERSON>Bill</PERSON>
</PEOPLE>
可以這樣處理此文檔:
XmlDocument doc = new XmlDocument();
doc.Load( "test.xml" );
XmlNode root = doc.DocumentElement;
foreach( XmlNode personElement in root.ChildNodes )
Console.WriteLine( personElement.FirstChild.Value.ToString() );
輸出為:
Fred
Bill
11.4.2 是否支持 SAX?
不。作為替換,提供了一個新的 XmlReader/XmlWriter API。像 SAX 一樣,它是基于流的,但它使用“pull”模型而不是 SAX 的“push”模型。這是一個例子:
XmlTextReader reader = new XmlTextReader( "test.xml" );
while( reader.Read() )
{
if( reader.NodeType == XmlNodeType.Element && reader.Name == "PERSON" )
{
reader.Read(); // Skip to the child text
Console.WriteLine( reader.Value );
}
}
11.4.3 是否支持 XPath?
是的,通過 XmlNavigator 類 (DocumentNavigator 是從 XmlNavigator 導(dǎo)出的):
XmlDocument doc = new XmlDocument();
doc.Load( "test.xml" );
DocumentNavigator nav = new DocumentNavigator(doc);
nav.MoveToDocument();
nav.Select( "descendant::PEOPLE/PERSON" );
while( nav.MoveToNextSelected() )
{
nav.MoveToFirstChild();
Console.WriteLine( "{0}", nav.Value );
}
11.5 線程
11.5.1 是否支持多線程?
是的,對多線程有廣泛的支持。系統(tǒng)能產(chǎn)生新線程,并提供應(yīng)用程序可以使用的線程池。
11.5.2 如何產(chǎn)生一個線程?
創(chuàng)建 System.Threading.Thread 對象的一個實(shí)例,把將要在新線程中執(zhí)行的 ThreadStart 示例傳遞給它。例如:
class MyThread
{
public MyThread( string initData )
{
m_data = initData;
m_thread = new Thread( new ThreadStart(ThreadMain) );
m_thread.Start();
}
// ThreadMain() is executed on the new thread.
private void ThreadMain()
{
Console.WriteLine( m_data );
}
public void WaitUntilFinished()
{
m_thread.Join();
}
private Thread m_thread;
private string m_data;
}
這里創(chuàng)建 MyThread 的一個實(shí)例就足以產(chǎn)生線程并執(zhí)行 MyThread.ThreadMain () 方法:
MyThread t = new MyThread( "Hello, world." );
t.WaitUntilFinished();
11.5.3 如何停止一個線程?
有好幾個辦法。首先,你能使用自己的通訊機(jī)制告訴 ThreadStart 方法結(jié)束。另外 Thread 類有內(nèi)置的支持來命令線程停止。基本的兩個方法是 Thread.Interrupt () 和 Thread.Abort ()。前者導(dǎo)致拋出一個 ThreadInterruptedException 并隨后進(jìn)入 WaitJoinSleep 狀態(tài)。換句話說,Thread.Interrupt 是一種禮貌的方式,它請求線程在不再進(jìn)行任何有用的工作時自行停止的。與此相對應(yīng),Thread.Abort () 拋出一個 ThreadAbortException 而不管線程正在做什么。而且,ThreadAbortException 不能像通常的異常那樣被捕獲 (即使最終將執(zhí)行 ThreadStart 的終止方法)。Thread.Abort () 是一般情況下不需要的非常手段。
11.5.4 怎樣使用線程池?
通過向 ThreadPool.QueueUserWorkItem () 方法傳遞 WaitCallback 的一個實(shí)例:
class CApp
{
static void Main()
{
string s = "Hello, World";
ThreadPool.QueueUserWorkItem( new WaitCallback( DoWork ), s );
Thread.Sleep( 1000 ); // Give time for work item to be executed
}
// DoWork is executed on a thread from the thread pool.
static void DoWork( object state )
{
Console.WriteLine( state );
}
}
11.5.5 怎樣知道我的線程池工作項目是在何時完成的?
沒有方法詢問線程池這類信息。你必須在 WaitCallback 方法中放置代碼來發(fā)出信號以表明它已經(jīng)完成。這里事件也很有用處。
11.5.6 怎樣防止對數(shù)據(jù)的并發(fā)訪問?
每個對象有一個與之相聯(lián)的并發(fā)鎖 (受批評的部分)。System.Threading.Monitor.Enter/Exit 方法用來獲得和釋放鎖。例如,下面類的實(shí)例只允許一個線程同時進(jìn)入方法 f ():
class C
{
public void f()
{
try
{
Monitor.Enter(this);
...
}
finally
{
Monitor.Exit(this);
}
}
}
C# 有一個關(guān)鍵字‘lock’提供了以上代碼的簡單形式:
class C
{
public void f()
{
lock(this)
{
...
}
}
}
注意,調(diào)用 Monitor.Enter (myObject) 并不意味著對 myObject 的所有訪問都被串行化了。它意味著請求同 myObject 相聯(lián)的同步鎖,并且在調(diào)用 Monitor.Exit(o) 之前,沒有任何其它線程可以請求該鎖。換句話說,下面的類和以上給出的類在功能上是等同的:
class C
{
public void f()
{
lock( m_object )
{
...
}
}
private m_object = new object();
}
11.6 跟蹤
11.6.1 有內(nèi)置的跟蹤/日志支持嗎?
是的,在 System.Diagnostics 命名空間中。有兩個處理跟蹤的主要的類—Debug 和 Trace。它們以相似的方式工作—不同之處是 Debug 類中的跟蹤只能在用 DEBUG 標(biāo)志生成的代碼中工作,而 Trace 類中的跟蹤只能在指明了 TRACE 標(biāo)記生成的代碼中工作。典型地,這意味著你應(yīng)該在你希望能在 debug 和 release 版本中都能跟蹤時使用 System.Diagnostics.Trace.WriteLine,而在你希望只能在 debug 版本中能跟蹤時使用 System.Diagnostics.Debug.WriteLine。
11.6.2 能否將跟蹤輸出重定向到一個文件?
是的。Debug 類和 Trace 類都有一個 Listeners 屬性,它們分別收集你用 Debug.WriteLine 或 Trace.WriteLine 產(chǎn)生的輸出。默認(rèn)情況下 Listeners 只有一個收集槽,它是 DefaultTraceListener 類的一個實(shí)例。它將輸出發(fā)送到 Win32 的 OutputDebugString () 函數(shù)和 System.Diagnostics.Debugger.Log () 方法。調(diào)試時這很有用,但如果你試圖從客戶站點(diǎn)跟蹤一個問題,將輸出重定向到一個文件中就更為恰當(dāng)。幸運(yùn)的是,為此目的提供了 TextWriterTraceListener 類。
這里是 TextWriterTraceListener 如何將 Trace 輸出重定向到一個文件:
Trace.Listeners.Clear();
FileStream fs = new FileStream( @"c:\log.txt", FileMode.Create, FileAccess.Write );
Trace.Listeners.Add( new TextWriterTraceListener( fs ) );
Trace.WriteLine( @"This will be writen to c:\log.txt!" );
注意使用 Trace.Listeners.Clear () 去掉了默認(rèn)的 listener。如果不這樣做,輸出將在文件和 OutputDebugString () 中同時產(chǎn)生。一般情況下你不希望如此,因?yàn)?OutputDebugString () 導(dǎo)致很大的性能開銷。
11.6.3 能否定制跟蹤的輸出?
是的。你能編寫你自己的 TraceListener 導(dǎo)出類,并把所有的輸出重定向到它上面。這里有一個簡單的例子,它從 TextWriterTraceListener 導(dǎo)出 (并隨后內(nèi)建了對寫文件的支持) 并在每個輸出行上添加時間信息和線程 ID:
class MyListener : TextWriterTraceListener
{
public MyListener( Stream s ) : base(s)
{
}
public override void WriteLine( string s )
{
Writer.WriteLine( "{0:D8} [{1:D4}] {2}",
Environment.TickCount - m_startTickCount,
AppDomain.GetCurrentThreadId(),
s );
}
protected int m_startTickCount = Environment.TickCount;
}
(注意這個實(shí)現(xiàn)并不完整—例如沒有覆蓋 TraceListener.Write 方法。)
這種方法的美妙之處在于,向 Trace.Listener 添加 MyListener 之后,所有對 Trace.WriteLine () 的調(diào)用都轉(zhuǎn)向了 MyListener,包括從對 MyListener 一無所知的被引用元件發(fā)出的調(diào)用。
12. 資源
12.1 從哪里可以獲得關(guān)于 .NET 的詳情?
Microsoft .NET 主頁位于 http://www.microsoft.com/net/。Microsoft 同時將它發(fā)布在 GOTDOTNET。
Microsoft 還發(fā)布了 .NET Framework FAQ,和本文很相似?梢栽谀抢镎业竭@里許多問題更“權(quán)威”的解答。
Robert Scoble 編輯了一個很容易理解的在線列表 http://www.devx.com/dotnet/resources/,這里還有一個 http://www.singularidad.com.ar/dotnet.asp。
在 http://www.devx.com/free/press/2000/vs-qalist.asp Robert 還有一個 .NET“著名問題與解答”主頁。
Richard Grimes 和 Richard Anderson 有一個叫作 Managed World.COM.的站點(diǎn)。
http://www.ibuyspy.com/ 是一個以展示 .NET 平臺為目的創(chuàng)建的示例站點(diǎn)。
還有我的 C# FAQ for C++ Programmers。
12.2 示例代碼和實(shí)用程序
Peter Drayton 的 .NET Goodies 主頁位于 http://www.razorsoft.net/
Don Box 的 CallThreshold 示例位于 http://www.develop.com/dbox/dotnet/threshold
Don 的 UnwindScope Service 位于 http://www.develop.com/dbox/dotnet/unwind
Don 的 CLR scripting host 位于 http://www.develop.com/dbox/dotnet/clrscript
Don 和 Jason 的 dm.net COM moniker 位于 http://staff.develop.com/jasonw/clr/readme.htm 在 http://www.bearcanyon.com/dotnet/ 有 Mike Woodring 的一些 .NET 例子。
在 http://www.cookcomputing.com/xmlrpc/xmlrpc.shtml 可以找到 Charles Cook 的 XML-RPC.Net library。