前言
Microsoft .net平臺組成
底層操作系統:
windows
:
--Microsoft
.NET Passport XML Web
服務支持
--即時消息通知應用程序
--.NET 企業服務器:
l
Microsoft Application Center 2000
l
Microsoft BizTalk Server 2000
l
Microsoft Commerce Server 2000
l
Microsoft Exchange 2000
l
Microsoft Host Integration Server 200
l
Microsoft Internet Security and Acceleration(ISA) Server 2000
l
Microsoft Mobile Information Server 2002
l
Microsoft SQL Server 2000
--Microsoft XML Web 服務:.NET My Services
http://www.Microsoft.com/MyServices/
開發平臺:.NET FrameWork
有兩個部分:通用語言運行時(Common Language Runtime:CLR)和.NET 框架類庫(Framework Class Library:FCL)
特征如下:
l
一致的編程模型。完全面向對象機制和豐富的FCL使編程模型一致
l
簡化的編程方式。簡化Win32和COM環境下所需的復雜基礎構造(注冊表、GUID、IUnknown、AddRef、Release、HRESULT等等)
l
可靠的版本機制。徹底消除“DLL hell”的大門
l
輕便的部署管理。安裝程序不再使用注冊表,而是直接進行拷貝
l
廣泛的平臺支持。對ECMA的CLR和FCL兼容的機器都可以運行
l
無縫的語言集成。遵循通用語言規范(Common Language Specification:CLS)書寫的類型可以在不同語言之間互用
l
簡便的代碼復用。
l
自動化的內存管理(垃圾回收)。對內存以及諸如文件、屏幕空間、網絡連接、數據庫等資源的管理
l
堅實的類型安全
l
豐富的調試支持。CLR完全支持跨語言調試
l
統一的錯誤報告。強大的異常處理機制;CLR中的異常具有跨模塊和跨語言特性
l
全新的安全策略。CLR中的代碼訪問安全(CAS)為我們提供了以代碼為中心的安全控制方式
l
強大的互操作能力。對訪問現有COM組件以、傳統DLL以及WIN32函數提供支持
集成開發環境:Visual Studio .NET
對所有的開發語言,有統一的IDE。
第一章
??
Microsoft .NET
框架開發平臺體系結構
一、
????????????
將源代碼編譯為托管模塊:
?
1、
?
CLR的存在是得我們可以選擇適合表達邏輯的語言,只要存在相應得編譯器將代碼編譯成為面向CLR的代碼即可,結果成為托管模塊。
2、
?
托管模塊(managed module):是一個需要CLR才能執行的標準windows可移植可執行文件(portable executable:PE),組成:
1)
???????
PE表頭:指出文件類型,文件的時間標記
2)
???????
CLR表頭:CLR版本,托管模塊入口、元數據、資源、強命名、標記等信息
3)
???????
元數據:源代碼中定義、引用的類型和成員
4)
???????
中間語言(IL)代碼
關于元數據:
l
????????
總與IL代碼同步
l
????????
省去源代碼編譯時對頭文件和庫文件的需求
l
????????
Visual Studio .NET利用之進行智能感知,輔助編碼
l
????????
用于CLR的代碼驗證
l
????????
序列化及反序列化對象
l
????????
垃圾收集器可以追蹤對象的生存期
二、
????????????
將托管模塊組合為程序集
關于程序集的理解:
暫歇
三、
加載通用語言運行時
1、
?
通過在%window%\system32目錄下查找MSCorEE.dll文件來判斷一個機器中是否安裝了.NET框架;而框架的版本可從注冊表下的子鍵:
HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ .NETFramework \ policy中查看
2、
當生成一個
EXE
程序集時,編譯器/鏈接器會產生一些特殊的信息,并將它們嵌入到結果程序集的
PE
文件表頭及其各個組成文件的
.text
部分。當
EXE
文件被調用時,這些特殊的信息將導致
CLR
被加載并初始化。
CLR
隨后會定位至應勝程序的入口點方法,從面以此來啟動應用程序。
?
類似地,如果是一個非托管應用程序通過調用
LoadLibrary
來加載一個托管程序集,那么該托管程序集
DLL
的入口點函數也會知道去加載
CLR
來處理包含在其中的代碼
3、
?
其他諸如
EXE
程序集或
DLL
程序集如何被執行的過程,可在考察普通
Windows PE
文件的執行過程后進行比較以明確執行過程
四、
執行程序集代碼
1、
?
IL代碼:一種面向對象的機器語言。可以理解對象類型,包含高級指令:創建和初始化對象、調用對象上的虛方法、直接操作數組元素、拋出和捕獲異常;
IL匯編語言能獲取CLR的所有功能,其他高級語言智能獲取其中的一個子集;
IL代碼獨立于CPU平臺,執行時要先被即時編譯器(Just In Time Compiler)編譯;
微軟提供的IL匯編器:ILAsm.exe 反匯編器:ILDasm.exe。
2、
?
IL代碼執行過程:
以下面代碼段為例:
??????? static void Main()
??????? {
?????????????? Console.WriteLine(“Hello”);
?????????????? Console.WriteLine(“GoolBye”);
}
?0
)代碼被執行之前:
???? CLR
首先檢測Main中代碼所引用到的所有類型,并分配一個數據結構記錄該類型,數據結構為類型的每個方法分配一個對應條目,記錄該方法實現代碼的地址;初始化該結構時,各方法被設置為CLR內部的一個函數(未定義的函數)
1)
???????
第一次被執行時
當上述結構中某函數被調用時,JITComplier由該函數的類型及地址信息在程序集的元數據中找到其IL代碼的位置,把這些IL代碼驗證并編譯成本地CPU指令這些CPU指令被保存在一個動態分配的內存中,而a)步驟中數據結構中被調用方法的地址則被替換為包含本地CPU指令的地址。
2)
???????
第二次被執行時
若被調用的函數已經被驗證并編譯過,則直接從內存中調用之
若沒有被調用則重復第一次被執行時的情況
3、
?
性能
托管代碼可能比非托管代碼效率更好的理由:
l
????????
JIT編譯器可檢測到新型CPU并產生為這些CPU提供的特殊指令,以優化執行效率
l
????????
JIT編譯器可檢測到總是返回錯誤的布爾測試,對于其所辦函的代碼段不產生CPU指令,以使得代碼量更小,執行效率更高。
l
????????
CLR在運行時有針對性地重新編譯某些IL代碼,重新組織以提高分支預測的成功率。
4、
?
NGen.exe提供折中方案,為IL代碼提供預編譯版本,以避免運行時編譯。
5、
?
IL代碼驗證:
--被驗證為“不安全”的代碼將拋出System.Security.VerificationException異常
--代碼驗證使得可以在一個windows進程中可以運行多個 托管應用程序
更多的討論在
20
章
五、
.NET框架類庫(FCL)
CLR和FCL允許開發人員創建以下幾種應用程序:
l
????????
XML Web服務
l
????????
Web窗體
l
????????
Windows窗體
l
????????
Windows控制臺應用程序
l
????????
Window服務
l
????????
組件庫
制作特定的應用程序,關鍵是對相應得FCL的熟悉,那么首先對.NET Framework熟悉了吧。
六、
通用類型系統(Common Type System)
描述了CLR中類型的規范
類型的成員組成:
l
????????
字段:表明對象的狀態
l
????????
方法:用來改變狀態
l
????????
屬性:提供對狀態訪問時輸入參數驗證、狀態有效性檢驗及必要的求值運算等功能
l
????????
事件:對象間的通知機制
類型可見性和訪問類型成員的一些規則:
l
????????
Private:只能被同一類型中的代碼訪問
l
????????
Family:可以被派生類中的代碼調用,而不管是否位于同一個程序集中
l
????????
Family與Assembly:只能被位于同一個程序集中的派生類代碼調用
l
????????
Assembly:只能被同一個程序集中的代碼調用
l
????????
Family或Assembly:能被任何程序集中的派生類代碼調用,也可以被同一程序集中的任何類型調用
l
????????
Public:可被任何程序集中的任何代碼調用
七、
通用語言規范(Common Language Specification)
為使一種語言創建的類型能被其他語言無縫訪問,微軟定義了通用語言規范(CLS)。CLR/CTS支持的特性比CLS定義的豐富得多,實際上不同的語言實現了CLR/CTS特性的一個子集,而CLS則是所有語言特性的交集(一個最小特性集合)。
因此只有遵循CLS的類型才能被其他語言訪問、應用,C#中使用:
[assembly:CLSCompliant(true)]
迫使編譯器確保在公共導出類型中,剔除不符合CLS的部分。
八、
與非托管代碼互操作
為避免重新實現所有現有代碼,CLR被設計成包含托管部分和非托管部分,CLR支持三種互操作情形:
l
????????
托管代碼調用DLL中的非托管函數
l
????????
托管代碼使用現存的COM組件
l
????????
非托管代碼使用托管類型
第七章
類型成員及其訪問限定
一、類型成員的可能組成:
ü
????????
常數,總是靜態不可變的
ü
????????
字段,分為靜態和實例字段兩種
ü
????????
實例構造器,初始化實例對象
ü
????????
類型構造器,初始化類型的靜態字段
ü
????????
方法,分為靜態和實例方法兩種
ü
????????
重載操作符,并非CLS的一部分,因為并非所有語言均支持之
ü
????????
轉換操作符,不是CLS的一部分,部分語言不支持
ü
????????
屬性,分靜態和實例屬性
ü
????????
事件,分為靜態和實例事件兩種
ü
????????
類型
二、訪問限定修飾符:[
僅列出c#
中的]
C#
術語
|
描述
|
private
|
僅可被所定義的類型(或其嵌套類型)訪問
|
protected
|
僅可以被所定義的類型(或嵌套類型)或繼承的類型訪問
|
Internal
|
僅可以被鎖定義的程序集訪問
|
protected internal
|
僅可以被所定義的類型(或嵌套類型)、派生類型以及同一程序集訪問
|
public
|
可被任何程序集的任何類型訪問
|
注:1、默認的訪問方式為Internal
?????? 2
、只能選擇上述的一種修飾符,而不可同時指定兩個
三、類型預定義特性:
C#
術語
|
描述
|
abstract
|
不可被實例化,可用作基類型
|
sealed
|
不能用作基類型
|
注:不可同時使用上述限定符,可通過為sealed修飾的類型提供private構造函數來達到“不可被實例化并不可被繼承”的目的
四、字段預定義特性:
C#
術語
|
描述
|
static
|
字段為 類型字段
|
readonly
|
僅可在構造器中被賦值
|
注:關于常數和靜態只讀字段的區別,將在第八章詳述
五、方法預定義特性
C#
術語
|
描述
|
static
|
類型方法,不能訪問實例字段或方法,只能訪問類型方法或類型字段
|
默認(CLR中稱為Instance)
|
實例方法,可以訪問實例方法或字段,也可以訪問類型方法或字段
|
virtual
|
多態的實現,總調用繼承鏈最末端的實現
|
new
|
僅用于虛方法,隱藏基類型的方法實現
|
override
|
僅用于虛方法,顯示聲明重寫基類型方法
|
abstract
|
僅用于虛方法,派生類必須提供和該抽象方法匹配的實現,含有抽象方法的類型為抽象類型
|
sealed
|
僅用于虛方法,派生類不能重寫該方法
|
注:sealed和abstract不能同時使用
第八章
常數與字段
一、常數
1、可被定義為常數的類型有:
2
基元類型:Boolean, Char, Byte, SByte, Decimal, Int16, Int32, UInt16, UInt32, Int64, UInt64, Single, Double
2
字符串:String
2
枚舉類型
2、常數在編譯后直接嵌入IL代碼中,因此一個模塊中的常數不能在運行時被另一模塊獲取,前者對常數進行的修改無法被另一個模塊在運行時感知,因此要想在運行時獲取“不變數值”應該使用只讀字段
二、字段
1、字段(包括靜態、實例或只讀字段)均在運行時分配內存
2、只讀字段只能在構造器中被符值(也可在聲明時被直接賦值,其他地方均不允許,實際上同于在構造器中賦值),靜態只讀字段在類型構造器中賦值,實例只讀字段在實例構造器中被符值。
3、靜態只讀字段只能使用類型名訪問,而不能使用實例引用訪問
三、靜態只讀字段和常數的區別:
1、常數直接編譯時刻嵌入IL代碼,在運行時不可重新讀取,靜態只讀字段則可以在運行時重新讀取
2、常數只能在聲明處賦值,而靜態只讀字段則可在構造器中賦值
第九章
方法
一、
????????????
實例構造器
1、
?
前面提到用new操作符創建對象時的三部曲:
l
????????
為對象分配內存
l
????????
初始化對象的附加成員(方法表指針和SyncBlockIndex)
l
????????
調用實例構造器初始化實例狀態
在分配內存時,系統將所有內存位置均置為0值,這就是為什么字段初始化而未賦值時均為0或null值。
不
調用
實例構造器的情況:
l
????????
調用Object.MemberwiseClone()方法創建實例(分配內存;初始化附加成員;將源對象字節拷貝到新創建的對象)
l
????????
反序列化對象時
2、
?
為避免為實例字段產生過多的構造器代碼,應避免在聲明字段時為字段符初值,而是在無參構造器中為它們符初值,在其他重載的構造器中調用無參構造器。
3、
?
值類型實例構造器
l
????????
C#編譯器不會自動調用其構造器,必須顯式調用構造器才能起作用
l
????????
C#編譯器不允許為值類型定義無參實例構造器(下面會介紹可以定義無參類型構造器)
l
????????
不能為結構中的字段在聲明的同時賦初值,可通過定義帶參構造器的方式進行
l
????????
必須在結構的構造器中為所有字段賦初值
二、
????????????
類型構造器
1、
?
類型構造器的一些限制:
l
????????
不能帶任何參數
l
????????
類型構造器總為私有的,不能用其他訪問修飾符
2、類型構造器被調用的時機:
l
????????
第一個實例被創建,或者類型的第一個字段或成員第一次被訪問之前
l
????????
非繼承靜態字段被第一次訪問之前
類型構造器在類型的生命周期中只被調用一次;
3、一些限制:
l
????????
若類型構造器中拋出異常,則該類型變成不可訪問,訪問其中的任何字段或方法均會拋出System.TypeInitializationException異常
l
????????
類型構造器只能訪問類型的靜態字段
l
????????
類型構造器不應該調用基類型的類型構造器,因靜態字段并非繼承而是編譯時靜態綁定
三、
????????????
操作符重載
1、
?
操作符重載
C#中對操作符重載的一些限制:
l
????????
必須聲明為public static
l
????????
必須有一個參數為操作符所屬類型
l
????????
不能改變操作符原始定義的引數個數
l
????????
若定義了true操作符也必須同時定義false操作符,二者都必須返回bool值
l
????????
++、--操作符必須返回其所隸屬之類型的一個實例
l
????????
可被重載的一元操作符:+、-、!、~、++、--、true、false
l
????????
可被重載的二元操作符:+、-、*、/、%、!、^(異或)、<、>、<<、>>、==、!=、<=、>=
l
????????
不允許被重載的操作符:&&、||、=、?:、+=、-=、/=、%=、|=、^=、<<=、>>=,實際上其中一些“復式操作符”在二元操作符被重載后自動生成,而不能顯式定義
l
????????
必須成對重載的操作符:(== ,!=)、(<,>)、(<=,>=)
l
????????
++、--操作符重載時不能區分其為前置或后置的
2、
?
操作符重載與語言互操作性
編譯器會為重載的操作符生成一個特殊名稱的方法,如+(加)操作符生成op_Addition()方法,并為該方法的定義條目上加上specialname標記。當某種語言不能進行操作符重載時,可以直接定義具有該特殊名稱的方法,以在其他語言中調用;或直接調用具有該特殊名稱的方法以適應某種語言不能解析操作符的限制。如:vb中不能重載操作符,可顯式定義op_Addition()方法以在C#中調用;C#中定義的+操作符不能被VB識別,可顯式調用op_Addition()方法獲得同樣的功能。
四、
????????????
轉換操作符
轉換操作符的一些限制:
l
????????
必須為public static
l
????????
必須指定關鍵字implicit或explicit,原則為:從本類型轉換為其他類型使用implicit,將其他類型轉換為本類型用explicit,不能都使用implicit
五、
????????????
方法參數
1、
?
引用參數
l
????????
缺省情況下為值傳遞
l
????????
標志為out的參數,在調用方法前
不必初始化,但返回之前必須賦值,沒有被初始化的參數是不能被使用
l
????????
標志為ref的參數,在調用方法前
必須初始化,否則觸發編譯錯誤
l
????????
可以使用ref或out來進行方法的重載,但不能通過區分ref和out來重載方法
l
????????
按引用方式傳遞的變量(實參)必須和方法聲明的參數(形參)類型完全相同,否則觸發編譯錯誤。
2、
?
可變數目參數
使用params關鍵字及對象數組的方式指定可變參數序列。一些限制:
l
????????
只有方法的最后一個參數才能使用可變數目參數
六、
????????????
虛方法
1、
?
虛方法的調用機理
CLR使用以下兩個IL指令調用方法:
u
??????
call
?
根據類型(即引用的靜態類型、聲明類型)來調用一個方法
u
??????
callvirt
????
根據對象(即引用的動態類型、實際類型)來調用一個方法
對于虛方法使用call來調用的情況有:
l
????????
base.虛方法(),
l
????????
密封類型引用虛方法,因為沒有必要檢驗密封類型的實際類型
l
????????
值類型,避免被裝箱
使用callvirt調用非虛方法的情況:
l
????????
應用變量為null時,使用callvirt才會拋出System.NullReferenceException異常,而call不會拋出
無論call或callvirt調用方法,均會有一個隱含的this指針作為方法的第一個參數,它指向正在操作的對象
2、
?
虛方法的版本控制:
用下面的例子說明:
using System;
class BaseClass
{
?????? public void NonVirtualFunc()
?????? {
????????????? Console.WriteLine("Non virtual func in base class");
?????? }
??????
?????? public virtual void VirtualFunc()
?????? {
????????????? Console.WriteLine("Virtual func in base class");??
?????? }
}
class DevicedClass : BaseClass
{
?????? //
若不使用new 關鍵字則編譯器會有warning:
?????? //
“DevicedClass.NonVirtualFunc()”上要求關鍵字
?????? //new
,因為它隱藏了繼承成員“BaseClass.NonVirtualFunc()”
?????? public new void NonVirtualFunc()
?????? {
????????????? Console.WriteLine("Non virtual func in deviced class");?????
?????? }
?????? //
若不添加關鍵字override或new,則編譯器會有warning:
?????? //
“DevicedClass.VirtualFunc()”將隱藏繼承的成員“BaseClass.VirtualFunc()
?????? //
”。若要使當前成員重寫該實現,請添加關鍵字 override。否則,添加關鍵字
?????? //new
。
?????? public override void VirtualFunc()
?????? {
????????????? Console.WriteLine("Virtual func in deviced class");?????
?????? }
}
class TestClass
{
?????? public static void Main()
?????? {
????????????? //
派生類實例調用 非虛 及 虛函數
????????????? DevicedClass dc = new DevicedClass();
????????????? dc.NonVirtualFunc();
????????????? dc.VirtualFunc();
????????????? //
基類實例調用 非虛 及 虛函數
?????????????
????????????? BaseClass bc = new BaseClass();
????????????? bc.NonVirtualFunc();
????????????? bc.VirtualFunc();
?????????????
????????????? //
指向派生類實例的基類引用 調用 非虛 及 虛函數
????????????? BaseClass bc1 = dc;
????????????? bc1.NonVirtualFunc();
????????????? bc1.VirtualFunc();
?????? }
}
/*
在虛函數上使用關鍵字override的運行結果:
Non virtual func in deviced class
Virtual func in deviced class
Non virtual func in base class
Virtual func in base class
Non virtual func in base class
Virtual func in deviced class
*/
/*
在虛函數上使用關鍵字new的運行結果
Non virtual func in deviced class
Virtual func in deviced class
Non virtual func in base class
Virtual func in base class
Non virtual func in base class
Virtual func in base class
*/
由上可見:new 和 override在派生類中協調版本的控制,在第七章中已經看到oeverride只能用于virtual方法,new則可用于非虛或虛方法,以實現隱藏基類中的同名方法。在虛函數上使用override,重寫了基類的方法,并無隱藏,這也就實現了多態。我們可設想這樣的結論:
new
使用
call
指令調用靜態類型的方法,而
override
使用
callvirt
指令調用動態類型的方法。
希望這個例子對您的理解有所幫助。
第十章
屬性
摘要:
本章討論C#中的 屬性 及 索引器
一、屬性
分為
靜態屬性、
實例
屬性和
虛
屬性
l
避免
直接訪問類型字段或使用煩瑣的
訪問器
方法進行訪問
l
很好的實現了類型的
數據封裝,如:改變字段而維持屬性的意義對用戶是透明的
l
代碼量小,運算量小的操作才使用屬性,否則使用方法調用更合適
二、索引器
l
可有多個重載的索引器,只要參數列表不同即可
l
可通過應用System.Runtime.CompilerServices.IndexerNameAttribute特性改變編譯器為索引器生成的方法名(缺省使用get_Item(…),set_Item(...))
l
不能通過上述改變方法名的辦法來定義多個參數列相同而僅名稱不同的索引器
l
沒有所謂“靜態索引器”
注:在屬性或索引器中添加對參數或value值得判定有助于保證程序的完整性
一個簡單的示例:
using System;
class IndexerTest
{
private static string[] strArr = new string[5];
IndexerTest()
{
for(int i = 0; i < 5; i ++)
{
strArr[i] = i.ToString();
}
}
public string this[Int32 nIndex]
{
get{
return strArr[nIndex];
}
set{
strArr[nIndex] = value;
}
}
//
提供不同的參數列進行重載索引器
public string this[byte bIndex]
{
get{
return strArr[bIndex];
}
set{
strArr[bIndex] = (string)value;
}
}
//
只讀屬性
public string[] StrArr
{
get{
return strArr;
}
}
public static void Main()
{
IndexerTest it = new IndexerTest();
it[1] = "Hello"; //
利用索引器進行寫操作
foreach(string str in it.StrArr)
{
Console.WriteLine(str);
}
}
}
<div style
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
