一、變量與表達式
1.變量第一個字母必須是字元、下劃線或@,其後就可以字元、數字、下劃線。
2.字面值
(1)double為浮點預設字面值,在給float或decimal浮點型指派時要加f/F或m/M。
3。表達式
Char變量實際存儲的是數字,是以把兩個char變量加在一起會得到一個數字。
二、流程控制
2.Switch
在C#中,執行完一個Case語句後,不能進入下一個Case語句,而就加個break或return 或goto語句,如若不然會報錯!但是有個例外,如果把多個Case語句放在一起,但是前面的Case語句沒有執行體,隻有最後一個Case語句有執行體,實際是一次檢查多個條件。也即隻要其中有一個符合,那麼最後一個Case語句的執行體就會執行!
例:
Case <comparison X>:
Case <compareson Y>:
<code>
Break;
這裡的X或Y必須是常量(字面值或字元常量)。
3.foreach語句
可以用foreach來查找string,可以用索引通路其字元。
Froeach(char c in mStrings){}
三、變量的更多内容
1.顯式轉換
(1)用Checked和uncheked檢測表達式的溢出,也就是将資料類型轉換為另一種資料類型時,可以知道是否有資料丢失。
(2)Convert指令顯示轉換時,它總是檢測溢出的。
(3)比Int類型低的整數類型(char,byte,shor等),進行運算後提升為int類型,是以必須用int或Long等長整數類型來儲存結果(當然用浮點也可以的)。而針對float和double不會出現這種情況,float運算可以用float類型存值。
(4)變量在指派後,這個變量才擁有記憶體空間,如果這種占據記憶體空間插座在循環中發生,該值實際上定義了一個局部值。在循環外部會超出作用域。一般情況下,最好在聲明和初始化所有的變量後,再在代碼中使用它們。
四、數組
1.數組聲明和初始化
由于數組是引用類型,是以應該用new來初始化數組。
(1)一維
Int[] a={1,2};//這樣也可以的。
Int[] a=new int[2]{1,2};//定大小(元素個數與數組大小必須比對)
Int[] a=new int[]{2,3,3};//由值定義大小
Int[] a=new int[size];//常量定義
Const int size=3;
(2)多元
Int[,] a=new int[2,2];
Int[,] a={{1,2},{2,3}};//
與C不同的是用數組初始化器(用字面值初始化)時,必須指定每個元素的值,不能遺漏。 針對一維數組可以用foreach來循環通路每個元素。
Foreach(int I in myInt){}
(3)變長數組
注意:子數組是不能指定的,而必須分開指定。
例:Int[][] a;
a=new int[2][];
a[0]=new int[4];
a[1]=new int[5];
或 a=new int[2][]{new int[]{1,2},new int[]{3,4}};
例:int[][] a={new int[]{1,2,3},new int[]{3,3,3}};
2Array類
此類是作為所有數組的虛基類,當用C#文法聲明了數組,實際上就繼承了它的方法和屬性。
屬性 說明
Length 傳回元素個數,傳回int型,如果多元數組,傳回所 有 階的元素個數
Longlength 傳回元素個數,隻是用Long型傳回的。
Rank 傳回數組的維數
常用方法
CreateInstance(),SetValue(),Sort()
3.疊代器
疊代器的定義是,它是一個代碼塊,按順序提供了要在foreach循環中使用的所有值,一般情況下,這個代碼塊是方法,但也可以使用屬性通路器和其代碼塊作為疊代器。
(1)如果要疊代一個類,使用方法GetEnumerator(),其傳回類型是IEnumerator就可以了。
(2)如果要疊代一個類成員,例如方法,則傳回類型IEnumerable。
在疊代器塊中,使用yeild關鍵字選擇要在foreach循環中使用的值。可以使用yield return;來中止傳回值。
Public static IEnumerable SimpleList()//疊代一個類成員
{
Yield return “str1”;
Yield return “str2”;
}
Foreach(string item in SimpleList){}
Public IEnumerator GetEnumerator()//如果類中存在這個方法并且傳回相應接口就可以疊代一個類。一般類重寫IEnumerable接口來實作,泛型和非泛型都實作
Yield return “ok”;
五、類
1.定義類
(1)類型通路修飾符隻能是public、internal(隻是程式集中可以通路),如果沒有加可通路性修飾符,預設為internal。
(2)類的抽象(abstract)或密封(sealed)
(3)不允許派生類的可訪性比基類更高,也即,内部類可以繼承公共類,公共類不能繼承内部類。
(4)當類既要繼承基類也實作接口時,必須把基類寫在前,接口放在後。
例如public class AClass:BClass,Itest
2.構造函數
(1)普通構造函數可以有通路修飾符。若有把構造函數設定為私有的,它在以下兩種情況下有用的:
類僅為某些靜态成員或屬性的容器,是以永遠不會執行個體化。
希望類僅通過調用某個靜态成員函數來執行個體化。
(2)若定義了帶有參數的構造函數,編譯器不會自動建立預設的無參構造函數,隻有沒有顯示定義任何構造函數時,編譯器才提供無參構造函數。
(3)靜态構造函數
作用:給類中靜态成員執行更複雜的初始化。
一個類中隻能有一個靜态構造函數,該構造函數不能有參數,不能有通路修飾符,它不能直接調用 ,隻能通路靜态類型成員,隻能下述情況調用:
1)建立包含該靜态構造函數的類執行個體時。
2)通路包含靜态構造函數的類的靜态成員時。
這兩種情況都會先調用靜态構造函數,然後執行其它的。無論建立了多了該類型執行個體,其靜态構造函數隻調用一次。
(4)構造函數執行序列
1)無論使用什麼構造函數執行個體化一個類,總是先調用System.object.object()
2)除非明确指定,否則就使用基類預設構造函數。
3)顯示調用基類的構造函數用base關鍵字,例:
Public MyClass(int I,int j):base(2){}
或public MyClass(int I,int j):base(i){}
4)本類構造函數調用本類另一個構造函數。
Public MyClass():this(6,7){}
Pulibc MyClass(int I,int j):base(i){}
當使用MyClass()構造函數時,先執行System.object.object(),接着執行基類帶一個參數的構造函數,再執行本類型帶兩個參數的MyClass(int I,int j),最後執行MyClass()。
3.成員定義
類中的資料成員有預設值。如果沒有顯示聲明成員的可通路性,編譯器通常預設選擇private(限制最大的那個)
(1)通路修飾符public,private,interal,protected适用于方法、字段、屬性、事件.
(2)字段、屬性、方法、事件都能用static聲明。
(3)字段若加上readonly關鍵字,表示它隻能由初始化語句或隻能在非靜态構造函數中指派。(隻加readonly屬于執行個體字段)
(4)用const聲明字段後,static就不能再加,否則報錯。它們都是類級别,都是靜态的存儲
private static string connectionString;可以在構造函數(靜态和非靜态)或屬性或初始化語句中指派!
private const string connectionString;隻能用初始化語句指派!
private static readonly string connectionString;這樣的字段也隻能在初始化語句中或靜态構造函數中指派,不能在非靜态構造函數或屬性中指派!
(5)靜态方法隻能通路靜态方法、靜态屬性、靜态字段,返過來非靜态成員是可以通路靜态成員的。
(6)以下關鍵字不能用于靜态方法或靜态屬性,想想就知道了為什麼!
virtual-方法可重寫。
Abstrct-方法必須在非抽象類中重寫(隻能用于抽象類中)
Override-方法重寫了一個基類方法(方法被重寫,必須使用)
Extern-方法定義放在其它地方
NEW-顯示說明隐藏基類方法
其中virtual Abstrct Override可以用在屬性中( 不是屬性塊)。
(7)如果重寫了一個方法,可以用sealed指定派生類中不能對這個方法作進一步的修改。也即孫子類不能再重寫此方法,本來是可以重寫的。
(8)顯示的調用基類的方法使用base關鍵字。
Public override void DoSomething()
{
Base.Dosomething();
}
由于base關鍵字是對象的執行個體,不能在靜态方法中使用,同樣也經常使用this關鍵字在方法中傳遞調用對象執行個體。
(9) protected internal:在同一個Assembly中,他的通路級别和public一樣,而跨Assembly通路時,他的通路級别同protected一樣。即protected的範圍 + internal的範圍。
(10)一個派生類型重寫在它的基類中定義的一個成員時,C#編譯器要求原始成員和重寫成員具有相同的可通路性。也即,如果基類中的成員是protected的,派生類是的重寫成員也必須是protected的。
4.方法
(1)引用參數(ref和out)
用ref時,在方法調用和方法聲明中都要指定reft,并且傳遞的變量必須是初始化後來變量,否則出錯。
用out時,首先,傳遞的變量可以事先沒有指派,其次,在方法中,該參數被看作沒有指派的,是以,在方法中必須對此參數指派,否則出錯(而ref不必須指派)。
(2)參數數組
這個參數必須是函數定義聽最後一個參數,它可以使用個數不定要參數調用函數,用params關鍵字定義。
例:void fun(params <type>[] <name>)
注意:如果給參數數組傳個數組,那麼就不能再傳遞任何參數了,否則報錯,當然調用此函數時也可以不傳遞任何參數。
5.屬性
(1)自動實作屬性
這種屬性會自動實作基礎成員變量。如
Public stirng ForeName(get;set;)
不需要聲明private string forename,編譯器會自動實作它。而且這兩個通路器(get,set)必須成對出現,缺一個不可。
(2)屬性中的get或set塊兒可以使用protected或private修飾符,public不可以,因為這兩個通路器修飾符必須比屬性具有更強的限制。
6.結構
(1)結構和類的差別
類和結構的差別是它們在記憶體中的存儲方式不同(類是存儲在堆上的引用類型,而結構是存儲在棧裡的值類型)
結構不支援繼承
構造函數的工作方式有一些差別。尤其是編譯器總是提供一個無參的預設構造函數,這是不允許替換的。
使用結構,可以指定如何在記憶體中布局。
(2)結構是值類型
結構是值類型那麼就有值類型的特征
例
Struct Test{pubic int width;public int height;}
Test t;
T.width=2;
T.height=3;
如果Test是個類,這種聲明後沒有配置設定空間就為其資料成員指派是會出錯的,但對結構是值類型,在聲明時就已經在棧裡配置設定了空間,是以可以為其成員指派了。
如果結構作為了類的資料成員,那麼結構變量的資料成員就具有預設值。和其它的預定義類型是一樣的。
(3)結構和繼承
結構不能繼承的,但是每個結構派生于System.valueType,System.ValueType派生于于System.object.是以可以重寫object的方法.
(4)我們不能像類那樣顯示去為結構定義無參構造函數,那是編譯器提供的。
(5)
class是引用類型,繼承自System.Object類;struct是值類型,繼承自System.ValueType類,是以不具多态性。但是注意,System.ValueType是個引用類型。
從職能觀點來看,class表現為行為;而struct常用于存儲資料。
class可以聲明無參構造函數,可以聲明析構函數;而struct隻能聲明帶參數構造函數,且不能聲明析構函數。是以,struct沒有自定義的預設無參構造函數,預設無參構造器隻是簡單地把所有值初始化為它們的0等價值
執行個體化時,class要使用new關鍵字;而struct可以不使用new關鍵字,如果不以new來執行個體化struct,則其所有的字段将處于未配置設定狀态,直到所有字段完成初始化,否則引用未指派的字段會導緻編譯錯誤。
class可以實抽象類(abstract),可以聲明抽象函數;而struct為抽象,也不能聲明抽象函數。
class可以聲明protected成員、virtual成員、sealed成員和override成員;而struct不 可以,但是值得注意的是,struct可以重載System.Object的3個虛方法,Equals()、ToString()和 GetHashTable()。
class的對象複制分為淺拷貝和深拷貝(該主題我們在本系列以後的主題中将重點講述,本文不作詳述),必須經過特别的方法來完成複制;而struct建立的對象複制簡單,可以直接以等号連接配接即可。
class執行個體由垃圾回收機制來保證記憶體的回收處理;而struct變量使用完後立即自動解除記憶體配置設定。
作為參數傳遞時,class變量是以按址方式傳遞;而struct變量是以按值方式傳遞的。
7.靜态類
隻能包含靜态成員。可以有靜态構造函數。
六、接口
1.定義接口
(1)類型通路修飾符隻能是public、internal(隻是項目中的代碼可以通路)或沒有。
(2)關鍵字abstract或sealed不能使用。
2.接口實作
(1)一個接口要隐藏基接口的成員,可以使用new來實作。
(2)接口中可以定義屬性(相當于方法)。
(3)接口成員定義:
不允許使用任何通路修飾符,全部都是公共的。
接口成員不能包含代碼體。
接口成員不通用static,virtrul,abstract或sealed來定義
接口不能定義字段成員
類型定義成員是禁止的
(4)類不論是實作接口還是繼承基類,重寫方法時都不能修改其原有的通路修飾符
(5)實作接口的方法時可用virtual或abstract,其它的都不能用。實作接口方法時override都不能用,否則出錯!!
(6)顯示執行接口成員
Public class MyClass:IMyInterface
{
Void iMyInterface.DoSomeThing()//顯示實作
Public void DoSomeThing(){}//隐式
}
顯示的執行接口成員,隻能通過接口通路,而隐式的類和接口都行。
即:
MyClass my=new MyClass();
IMyInterface myInt=my;
MyInt.DoSomething();//接口通路
My.DoSomeThing();//類通路
實際上顯示實作接口方法時,類執行個體根本無法調用此方法。
(7)接口定義屬性時,可以隻有一個set或get塊,在類實作時,那麼是可以添加缺少的那個塊的,不過其通路屬性要加強,比接口中定義的通路器的通路必更嚴格。
接口小結:接口可以繼承基接口。它隻能有方法和屬性。在方法和屬性前通常不能有任何關鍵字(派生接口要隐藏其接口的方法時可以用new除外)。
七.繼承
1.繼承類型
實作繼承:一個派生于一個基類型。
接口繼承:一個類實作接口的方法。
2.結構總是派生于System.ValueType,它們還可派生于任意多個接口。
類總是派生于使用者選擇的另一個類,這們還可以派生于任意多個接口。
八、運算符和強制類型轉換
測試類型用typeof()運算符或用對象的GetType()方法。
1.布爾邏輯
(&,|)這兩個作為邏輯運算符時,它兩邊的操作數總要計算的。
2.封箱和拆箱
封箱就是把值類型轉換為對象類型或其實作的接口類型,拆箱就是相反的過程。
Int b=2;
Object val=b;//封箱,自動的,
Int c=(int)val;//拆箱,顯示的
在裝箱和拆箱的過程中,這兩個過程都會資料複制到新裝箱和拆箱的對象上,這樣不會影響原來的值類型的内容。
封箱的作用主要展現在兩方面,一個把值類型加入到集合中,如ArrayList,集合中的項是object,其次,有一個内部機制允許在值類型上調用相應對象的方法和屬性,如整型和結構。例, 10.toString();這裡把10先裝箱為一個臨時對象,然後調用該對象的方法。
3.is運算符
<opernand> is <type>
這個表達式結果如下:
(1)如果<type>是一個類類型,而<operand>也是該類類型,或是繼承該類類型,或可以封箱到該類型中,則為true..
(2)如果<type>是一個接口類型,而<operand>實作了該接口的類類型,或者也是該類型。則為true
(3)如果<type>是一個值類型,而<operand>也是該類型,或者可以拆箱到該類型。則為true
4.as運算符
把一種類型轉換為引用類型。
<operand> as <type>
(1)如果<operand>類型是<type>類型
(2)<operand>類型可以隐式轉換為<type>類型
(3)<operand>可以封箱到<type>類型。
如果不能從<operand>轉換為<type>類型,則表達式的結果為null.
5.可空類型?
通常可空類型與一進制或二進制運算符一起使用,如果其中一個操作數或兩個操作數為null,其結果為null. 例:
Int? a=null;
Int? b=a+3;//b=null
可空類型的運算結果還是可空類型,如果用非可空類型來接收結果值會出錯。
Int? a=2;
Int resul=a*2;//報錯
可用強制類型轉換
Int result=(int)a*2;
在比較可空類型時,隻要有一個操作數為null,結果為false。
6.空接合運算符??
第一個操作數必須是可空類型或引用類型,第二個操作數必須與第一個操作數類型相同,或可以隐式轉換為第一個操作數類型。
運算方法如下:如果第一個操作數不是null,則整個表達式的值等于第一個操作數的值,否則整個表達式的值為第二個操作數的值。運算結果可以用非可空類型變量來接收值!
Int?a =10;
Int b=a ?? 2;//b=10
Int b=a ?? 3;//b=3
7.類型轉換
短位元組向長位元組類型可以隐式轉換,反之必須顯示轉換。
在隐式轉換值類型時,可空類型要額外考慮。
(1)可空類型轉換為其它可空類型時遵循短位元組向長位元組的轉換規則。
(2)非可空類型轉換為可空類型時遵循短位元組向長位元組的轉換規則。
(3)可空類型不能隐式轉換為非可空類型,必須顯示轉換。
8.運算符重載
(1)重載的運算符是公共和靜态的函數。
例:
Public static Vector operator +(Vector v1,Vector v2)
(2)比較運算符重載
==和!=,<,>,>=,<=,true,false
這些運算符要求必須成對的重載。而且傳回值必須為bool類型。
9.使用者定義資料類型轉換(類和結構)
Public static implicit operator float(Currency value){}
Public static explicit operator Currency(float value){}
這裡有兩個關鍵字可以指定這種轉換是顯示的還是隐式的!緊接着在operator關鍵字後的 資料類型表式轉換的目标類型。上例中Currency是一個結構。
(1)類之間的資料類型轉換
如果一個類直接或間接繼承了另一個類,就不能定義這兩個類之間的資料類型轉換(這些類型的已經存在)。
資料類型轉換必須在源或目标資料類型的内部定義。
一旦在一個類的内部定義資料類型轉換,就不能在另一個類中定義相同的資料類型轉換。
九、C#3.0語言的改進
1.類初始器
文法:
ClassName variableName=new classname{
PropertyOfField1=value1,
PropertyOfField2=value2,
。。。。。。
使用對象初始器時,不能明确調用類的構造函數,而是自動調用預設的無參構造函數。這個調用是在初始化器設定參數之前調用的,是以必須能通路類的預設構造函數。
2.集合初始化器
就是數組初始化器的一個擴充,或者說是原來在集合中沒有,現在加上了。
List<int> myint=new List<int>{2,3,3};
3.類型推斷
使用var關鍵字,編譯器根據變量的初始值來“推斷”變量的類型。
用var聲明變量的同時必須初始化變量,否則報錯
Var a=0;
Int a=0;
編譯後上面兩條語句是等價的。
Var關鍵字還可以通過數組初始器來推斷數組的類型。
Var myArray=new[] {2,3,3};
此例會把myArray的類型被隐式地設定為int[].
但在初始化器中使用的數組元素必須是:
相同的類型
相同的引用類型或為空。
所有元素的類型都可以隐式地轉換為一個類型
元素可以轉換的類型就稱為數組元素的最佳類型。
4.匿名類型
匿名類型隻是一個繼承了Object的、沒有名稱的類。該類的定義從初始化器中推斷。
Var caption=new {FirstName=”2b”,MiddleName=”3b” };
這會生成一個包含FirstName,MiddleName屬性的對象,如果建立另一個對象
Var doctor= new {FirstName=”2b”,MiddleName=”3b” };
Caption和doctor的類型是相同的。還可以使用一個已經存在的對象來初始化匿名對象。包含相應的屬性FirstName,MiddleName.
Var caption=new {person.FirstName,person.MiddleName};
這些對象的類型是未知的,編譯器為類型”僞造”一個名稱 。但隻編譯器才能使用它。
注意,這此屬性定義為隻讀屬性,這表示,如果要在資料存儲對象中修改屬性的值,就不能使用匿名類型。
十、委托和事件
1.委托
(1)委托就是取代了在C或C++中的把一個函數傳遞給另一個函數,而在C#中委托是一種特殊的對象類型。
Delete void IntMethodInvoker(int i);
上面就是一個委托的示例,它指定了每個委托的執行個體都包含了一個方法的細節,該方法有一個int參數,并傳回void。編譯器将在背景建立一個表示該委托的一個類。由于定義一個委托就基本上就是定義了一個類,是以既可以在類的内部定義委托,也可以在類的外部定義委托,還可以在命名空間中把委托定義為頂層對象。可以為委托加上通路修飾符:public,private,protected
Private delete string GetString();
Static void main()
Int x=20;
GetString firstMethod=new GetString(x.toString);//用委托構造函數方式傳遞方法
或
GetString firstMethod=x.toString;//直接把方法的位址賦給委托變量
Console.write(firstmethod());//調用委托就像調用方法一樣,有參傳入參數
以上就是定義委托和使用的委托的例子。
注:給定委托的執行個體可以表示任何類型的任何對象上的執行個體方法或靜态方法—隻要方法的簽名比對委托的簽名就可以了。
(2).多點傳播委托
委托可以包含多個方法,這種情況下,委托隻能傳回void,否則,就隻能得到委托調用的最後一個方法的結果。
DoubleOp operations = MathOperations.MultiplyByTwo;
operations += MathOperations.Square;
operations(2.3);//它會把兩個方法都調用執行了。
在這裡多點傳播委托添加了兩個操作,多點傳播委托可以識别運算符+,+=,将方法添加到委托中,而運算符-,-=将方法從委托中删除。
當調用多點傳播委托時,它會把添加到委托中的所有方法全部執行一遍!但是這個執行順序鍊是未定義的,也即它執行這些方法的順序是不定的。
多點傳播委托包含一個逐個調用的委托集合,如果通過委托調用的一個方法抛出出了異常整個疊代停止。為了避免這個問題,應手動疊代方法清單。
Delegate[] delegates= operations.GetInvocationList();
Foreach(DoubleOp d in delegates)
{
(3).匿名方法
Delegate string DelegateTest(string param);
Static void Main()
String mid=”ok”;
DelegateTest test=delegate(string val)
Val+=mid;
Return val;
Console.write(test(“lai”));
匿名方法必須遵循兩個規則:首先,匿名方法不能用跳轉語句跳轉到匿名方法的外部,反之亦然。其次,不能通路在匿名方法外部使用的ref 和out參數,但可以外部其它變量。
(4)λ表達式
λ表達式是簡化匿名方法的一種方式。
λ表達式的參數清單總是包含一個逗号分隔的清單,其中的參數要麼顯示類型化,要麼都是隐式類型化的。如果隻有一個參數,就可以省略括号,否;否則需要加上括号。
DelegateTest my=(int paramA,int paramB)=>paramA+paramB
或(paramA,paramB)=>paramA*paramB
DelegateTest my =Param=>paramA-paramB
上面的例子的語句體沒有用return關鍵字指定傳回值,根據指定的操作,編譯器會推斷出表達式的結果類型就是傳回類型。
還可以指定沒有參數的λ表達式,但必須加上括号
例()=>Math.Pi
λ表達式語句體,如果語句體隻有一條語句,可以去掉花括号,多條語句必須加上花括号。
DelegateTest my =Param=>{return paramA-paramB;}
(5)協變和抗變(跟C++裡的很像)
也就是說委托調用的方法不一定和委托聲明定義的類型相同,有兩種情況,協變和抗變。主要針對對象類型。
(1)傳回類型協變
方法的傳回類型派生于委托的傳回類型。
(2)參數類型抗變
委托類型的參數和委托調用的方法的參數不同。委托的參數類型可能派生于方法的參數類型。
2.事件
(1生成事件
Public delegate void ActionEventHandle(object sender,ActionCancelEventArgs e);
Public static event ActionEventHandle Action;
注意定義一個新的事件時,要求指定與事件相關的委托,除了可以使用.net framework中定義的委托外,還可以用自定義的委托,這裡就是用的自定義委托。
除了上面那種定義事件的方法外,還可以定義添加和删除處理程式的方法。
Private ActionEventHandle act//先聲明一個委托類型變量
Public event ActionEventHandle Action
{
Add{
act +=value;
}
Remove{
act -=value;
}
有點相當于把事件定義為一個屬性。
(2)調用事件
例
Form1.Action += new Form1.ActionEventHandler(Form1_Action);
ActionCancelEventArgs cancelEvent = new ActionCancelEventArgs();
Action(this, cancelEvent);
上面的例子就是直接用事件變量來調用,上面調用事件就像調用方法一樣,我感覺就是取代了委托來調用方法。
小結:調用自定義事件的步驟,首先,定義委托和事件,然後,用委托注冊事件,其次建立自定義的事件參數對象,最後傳遞相關的參數給事件來觸這個事件。
十一、字元串和正規表達式
1字元串
一般用stringbuilder執行字元串操作,string類來存儲字元串或顯示結果。
2正規表達式
.net正規表達式引擎是相容perl5正規表達式的,加了一些新特性。
十二、泛型
1.定義泛型類
(1)不能假設泛型類型是什麼類型,是以不能在泛型類中做某些特定類型的操作,如:
private T1 innerT1;
T1=new T1();//是值類型還是泛型類型?不知道,是以不能用new關鍵字。
(2)注意另一個限制,在比較泛型類型提供的類型值和null時,隻能使用運算符==和!=。
由于不知道泛型類型,其它的運算符不好用的。
2.泛型類的特性
(1)預設值
泛型類中不能把null賦給泛型類型,因為泛型類型既可以引用類型也可以值類型,是以這裡要用default關鍵字賦給泛型類型,則default把null賦給引用類型,把0賦給值類型。
(2)限制
如果泛型類調用泛型類型上的方法,就必須添加限制。也即指定泛型類型必須實作什麼接口等。
例:
Public class DocumentManager <TDocument> : IDocumentManager<TDocument>
where TDocument: Idocument
這裡用where關鍵字指出泛型類型TDocument必須實作IDocument這個接口。還可指定多個where子句
class MyClass<T1,T2>:baseClass where T1:iFoo where T2:new()
限制類型小結:
限制 說明
Where T:struct 結構限制,類型T必須是值類型(直接使用struct)
Where T:class 類型T必須是引用類型(使用時直接使用class)
Where T:IFoo 類型T必須實作接口IFOO(實際接口名字)
Where T:Foo 類型T必須派生于基類Foo(實際類名字)
Where T:new() 類型T必須有一個預設構造函數(直接使用new())
Where T:U 類型T1派生于泛型類型T2
注意:CLR2.0中,隻能為預設構造函數定義限制!
以上的限制可以合并一起指定。
例:public class Myclass<T> where T:class,new()
注意,如果new()用作限制,它就必須是為類型指定的最後一個限制。
提示:在C#中,where子句不能定義必須由泛型類型執行的運算符,運算符不能在接口中定義,在where子句中,隻能定義基類、接口和預設構造函數(不懂?)
(3)繼承
Public class Base<T>{}
Public class Derived<T>:Base<T>{}
或
Public class Derived<T>:Base<Int>{}
Public calss Derived:Base<string>{}
就是基類要麼和子類相同的泛型類型,要麼指定一個類型。
而且子類可以是泛型也可以非泛型。
(4)靜态成員
泛型類的靜态成員與非泛型類靜态成員不同,非泛型類是類的所有執行個體共享一個靜态字段,但是泛型類中是每個泛型類的執行個體獨享一個靜态字段!
3.泛型接口
也就那麼回事吧,沒有泛型類東西多1
4.泛型方法
隻針對類中的某個方法(靜态或非靜态均可)使用泛型而不是整個類。其使用定義方式和定義泛型類很相似,可以指定預設值,限制。
例:
Void Swap<T><ref T x,ref T y){}
調用時可以用兩種方式
Int i=2
Int j=3;
Swap<int>(ref I,ref j);
或swap(ref I,ref j);
第二種方式由于c#編譯器會通過調用 Swap方法來擷取參數的類型,是以不需要把泛型類型賦予方法調用。
5.泛型委托
它的定義就像泛型方法一樣
public delegate TSummary Action<TInput, TSummary>(TInput t, TSummary u);
6.net Framework其它泛型類型
(1)Nullable<T>
可空類型
Nullable<int> a;
Int? a;
上面兩個等價的
(2)EventHandler<TEventArgs>
(3)ArraySegment<T>
十三、集合
非泛型集合類基本不用了!我認為
1.常用泛型集合
List<T>,Queue<T>,Stack<T>,
LinkedList<T>(連結清單),
SortedList<TKey,TValue>(有序連結清單)
如果需要排好序的表,可以使用這個類,這個類型按照鍵給元素排序,它用鍵作為索引,實作基于數組。雖然用到了鍵,可不是基于字典!
1.1Dictionary<TKey,TValue>(字典)
工作方式:将一個鍵添加到字典中,鍵會轉換為一個散列。利用散列建立一個數字,它将索引和值關聯起來。然後索引包含一個到值的連結。
(1)鍵的類型
用作字典中鍵的類型必須重寫object類的GetHashCode(),字典的性能取決于GetHashCode()方法的實作, GetHashCode方法必須滿足如下要求:
相同的對象應總是傳回相同的值
不同的對象可以傳回相同的值
不能抛出異常
應至少有一個執行個體字段
運作得要快
散列碼值應平均分布在int可以存儲的整個數字區域上
散列碼最好在對象的生存期不發生變化
鍵類型還必須實作IEquality.Equals(0訪求或重寫object類的Equals()方法,因為不同的對象可能傳回相同的散列碼,是以字典是用Equals()方法來比較鍵的。
一般用string類型用作鍵值很好,而用整數用作鍵值不好!
1.2Lookup<Tkey,Telement>
這個泛型為。NET3.5新增的,它把鍵映射到一個值集上。
1.3 SortedDiectionary<TKey,TValue>
這個泛型和SortedList<TKey,TValue>功能相似,但SortedList<TKey,TValue>實作是基于數組的連結清單,而SortedDiectionary<TKey,TValue>
實作為基于一個字典。
但有不同的特性
SortedList<TKey,TValue>使用的記憶體比SortedDiectionary<TKey,TValue>少
SortedDiectionary<TKey,TValue>插入和删除速度比較快。
1.4HashSet<T>泛型(.net 3.5新增)
這個泛型集合類包含不重複項的無序清單。這種集合叫”集set”,這個集合基于散列值,插入元素的操作非常快。跟數學上的集合一樣的,交集和并集什麼的!具體方法檢視MSDN!
合并一個集合,重複值被去掉了等!
十四、反射
C#.Net 使用經驗
1.    System.Diagnostics.Process.GetCurrentProcess().MinWorkingSet = new System.IntPtr(5)用這個方法來強制釋放記憶體,很管用,很好很強大!我放在了用了webbrower控件的函數中!但是我發現它把記憶體中的資料放在了虛拟記憶體中,但是虛拟記憶體基本不怎麼長,看來還是有釋放掉的!
2.    C#可選參數沒有傳給它值時,它不為NULL,是一個數組,判斷時用Length屬性.
3.    預定義字元類可以放在普通字元類中,如:[\w],根據原來的意義進行比對!
4. Effective C#中說的構造函數的順序:
1、靜态變量存儲設定0。
2、靜态變量初始化器執行。
3、基類的靜态構造函數執行。
4、靜态構造函數執行。
5、執行個體變量存儲設定0。
6、執行個體變量初始化器執行。(聲明時初始其值)
7、恰當的基類執行個體構造函數執行。
8、執行個體構造函數執行。
但是經測試的順序是這樣的。
1ã 派生類靜态變量存儲設定0。
2ã 派生類靜态變量初始化器執行。
3ã 派生類靜态構造函數執行。
4ã 派生類執行個體變量存儲設定0。
5ã 派生類執行個體變量初始化器執行。
6ã 基類的靜态變量存儲設定0
7ã 基類靜态變量初始化器執行
8ã 基類靜态構造函數執行
9ã 基類執行個體變量存儲設定0
10ã     基類執行個體變量初始化器執行
11ã     恰當的基類執行個體構造函數執行
12ã     派生類執行個體構造函數執行
5. .net序列化時,如果是xml序列化,基本上可以在類上不加任何的特性,而是二進制或SOAP序列化必須在類上加Serializable特性。否則報異常。