《C#與.NET3.0高階程式設計》摘要-----第三章 C#語言基礎
阿新 • • 發佈:2019-01-04
第三章 C#語言基礎
3.1、剖析一個簡單的C#程式
1.簡單示例
C#要求所有的程式邏輯都包含在一個型別定義中。 using System; class HelloClass { public static int Main(string[] args) { Console.WriteLine("Hello World!"); Console.ReadLine(); return 0; } } 需要理解:公共成員能通過其他型別訪問;靜態成員的作用於在類級別上而非物件級別上,並且不需要事先建立一個新的類例項就能被呼叫。 C#區分大小寫。C#關鍵字都是小寫的,名稱空間、型別和成員名稱以一個大寫字母開頭,中間的單詞是首字母大寫。2、Main方法的其他形式
3、處理命令列引數
可以直接訪問args,用for或foreach遍歷。 也可以通過System.Enviroment型別的靜態方法GetCommandLineArgs()來訪問,返回的是字串陣列,第一個索引表示包含應用程式本身的當前目錄,其餘的元素包含單獨的命令列引數。使用時,不用再將Main()定義成一個帶字串陣列引數的方法。 使用VS2005指定命令列引數:Solution Explore裡的Debug那裡指定Command line arguments。3.2、System.Enviroment類
3.3、初步瞭解類及物件
與C++不同,C#不允許將類型別分配到棧上,因此必須使用new關鍵字來建立物件。 事實上,C#的物件變數是對記憶體中物件的引用。1、有關建構函式
預設建構函式會為每一個成員資料設定一個合適的預設值。支援過載建構函式。 一旦為一個型別定義了自定義的建構函式,預設建構函式就被刪除了。如果希望允許物件使用者使用預設建構函式建立型別的實力,需要顯示的重定義它。2、C#沒有解構函式
3、定義“應用程式物件”
有關“分工:(separation of concerns)。類應該只定義自己相關的功能,呼叫自己的最好放到另外一個類中。例如HelloClass類和HelloApp類。 也就是說,最好不要在main方法裡建立定義自己的那個型別的例項。3.4、System.Console類
該類封裝了基本控制檯應用程式的輸入、輸出和錯誤流操作。 表3.1:該類在.NET2.0中特有的成員成員 | 作用 |
BackgroundCokor ForegroupColor |
設定當前輸出的背景/前景色。可以被賦予ConsoleColor列舉的任何成員。 |
BufferHeight BufferWidth |
控制控制檯緩衝區與的現實區域 |
Clear() | 清楚快取和控制檯的標題 |
Title | 設定當前控制檯的標題 |
WindowHeight WindowWidth WindowTop WindowLeft |
控制與已建立的緩衝區相關的控制檯大小 |
1、使用Console類進行輸入和輸出
該類的輸入輸出方法都被定義成靜態的。
可以用大括號數字編碼來制定可選佔位符。比如下面幾種方法: Console.WriteLine("Int is: {0}/nDouble is: {1}/nBool is: {2}", theInt, theDouble, theBool); object[] stuff = {"Hello", 20.9, 1, "There", "83", 99.99933}; Console.WriteLine("The Stuff is: {0}, {1}, {2}, {3}, {4}, {5}", stuff); Console.WirteLine("{0}, Number {0}, Number {0}", 9);2、.NET字串格式化標誌
表3.2 .NET字串格式字元字串格式字元 | 作用 |
C或c | 用於格式化貨幣。預設情況下,這個表示會以當地的貨幣符號作為字首。 |
D或d | 用於格式化十進位制數。這個標記還用於指定填充值的最小個數。 |
E或e | 用於指定記數法。 |
F或f | 用於定點小數的格式化。 |
G或g |
代表general。這個字元用來格式化一個數為定點或指數格式。 |
N或n | 用於基本的數值格式化(帶逗號)。 |
X或x | 用於十六進位制格式化。如果使用大寫的X,十六進位制格式也會包含大寫的字元。 |
3.5、設定成員可見性
對於給定類或結構的一個成員(方法、欄位、建構函式等)必須指定它們的“可見性”級別。 如果為顯示指定,則預設為private。 表3.3 C#的可訪問性關鍵字C#訪問修飾符 | 作用 |
public | 成員既可以從一個物件變數訪問,又可以從任何派生類訪問。 |
private | 成員僅能被這個類的方法訪問。所有的成員預設為private。 |
protected | 成員既可以在定義它的類中使用,又可以在任何派生類中使用。然而,它不能從物件變數訪問。 |
internal | 成員可以被同一個程式集內的任何型別訪問,但是不能被程式集外被任何型別訪問。 |
protected internal | 成員的訪問被限制在當前程式集,或者當前程式集中從定義它的類所派生的型別中。 |
class SomeClass { // Accessible anywhere. public void PublicMethod() { } // Accessible only from SomeClass types. private void PrivateMethod() { } // Accessible from SomeClass and any descendent. protected void ProtectedMethod() { } // Accessible from within the same assembly. internal void InternalMethod() { } // Assembly-protected access. protected internal void ProtectedInternalMethod() { } // Unmarked members are private by default in C#. void SomeMethod() { } }
1、有關型別可見性
型別(類、介面、結構、列舉和委託)也可以帶訪問修飾符,但是隻能用public或internal。 型別預設為internal的。3.6、類成員變數的預設值
1、類型別的成員變數
bool型別被設定成false。 數值型別被設定為0,如果是浮點資料型別的話為0.0。 string型別的被設定為null。 char型別的被設定為'/0'。 引用型別被設定成null。2、區域性變數
當定義區域性變數的時候,它們不接受預設賦值,必須在使用它們之前賦一個初始值。 有一個例外,如果這個變數用作輸出引數,則不需要被賦予初始值。3.7、成員變數的初始化語法
類型別的成員可以在定義該成員時賦予初始值,但是要注意成員的複製發生在建構函式之前,因此如果在建構函式裡又給一個欄位賦值,那麼實際上就取代了前面的成員賦值。3.8、定義常量資料
使用const關鍵字,用來定義不應該被再次賦值的資料。不同於C++,在C#中const關鍵字不能被用來限定引數或返回值,而被保留用來建立區域性或例項一級的資料。 要賦給常量的值必須在編譯期間就知道的,因此常量成員不能被賦給物件的引用。 必須在宣告時直接賦值,而不能放到建構函式裡。 如果用ildasm來檢視這些常量,會發現這些值是直接硬編碼到程式集中的。 常量欄位隱含為靜態的,因此如果引用外部型別定義的常量,需要以定義它的型別的名字開頭。3.9、定義只讀欄位
如果需要建立一個執行前不知道初始值且不可改變的欄位時需要用到,比如建立一個類型別的例項。 使用關鍵字readonly。 與常量資料不同的另一個地方是:它們的值可以在建構函式的作用於內被指定。當要賦給只讀欄位的值必須從外部源讀取時是很有用的。 class Emplyee { public readonly string SSN; public Employee(string empSNN) { SSN = empSSN; } } 只讀欄位並不是靜態的。3.10、static關鍵字
1、靜態方法
靜態方法只能操作類的靜態成員。2、靜態資料
只分配一次,在所有物件例項之間共享。非靜態方法也可以訪問修改靜態資料。3、靜態建構函式
用來對靜態資料初始化。 class SavingAccount { ... static SavingAccount() { Console.WriteLine("In static ctor!"); currInterestRate = 0.04; } } 幾個特性: Ⅰ、一個給定的類(結構)只能定義一個靜態建構函式。 Ⅱ、靜態建構函式僅執行一次,與建立了多少這種型別的物件無關。 Ⅲ、靜態建構函式不帶訪問修飾符,也不帶任何引數。 Ⅳ、當靜態建構函式建立這個類的例項時,或者在呼叫者訪問第一個靜態成員之前,執行庫會呼叫靜態建構函式 Ⅴ、靜態建構函式在任何例項級別的建構函式之前執行。4、靜態類
C#2.0新增。 只能包含靜態方法和靜態資料。不能用new建立。 c#2.0之前,防止建立這類資料的方法一是重定義預設建構函式為私有或用abstract將類標記為抽象型別,但是不是型別安全的。3.11、方法引數修飾符
表3.4 C#引數修飾符引數修飾符 | 作用 |
無 | 預設為按值傳遞(pass by value),意味著被呼叫的方法收到原始資料的一份副本。 |
out | 輸出引數是由被呼叫的方法賦值的,因此是按引用傳遞(pass by reference)的,如果被呼叫的方法沒有給輸出引數賦值,會出現編譯錯誤。 |
params | 該引數允許將一組可變個數的相同型別的引數作為單獨的邏輯引數進行傳遞。方法只能有一個params修飾符,而且必須是方法的最後一個引數。 |
ref | 呼叫者賦初值,且可以由被呼叫的方法可選的重新複製,資料是按引用傳遞的。 |
1、預設的引數傳遞
按值傳遞,被呼叫者對引數的改變不會影響到呼叫者。2、out修飾符
呼叫者和被呼叫者都需要新增out關鍵字。如果呼叫者在呼叫被呼叫者之前對變數進行了賦值,那麼該值在呼叫後將會消失。
一個顯而易見的好處就是使用out引數可以讓呼叫者只使用一次方法呼叫就能獲得多個返回值。
3、ref修飾符
如果希望方法可以對在呼叫者作用域中宣告的不同資料進行操作,就必須使用引用引數。輸出引數和引用引數有一些區別:
Ⅰ、輸出引數不需要在它們被傳遞給方法之前初始化。 Ⅱ、引用引數必須在它們被傳遞給方法之前進行初始化。
4、params修飾符
它可以用來建立一個方法,接受一組具有相同型別的引數,但作為一個邏輯引數。// params keyword. // Return average of ‘some number’ of doubles. static double CalculateAverage(params double[] values) { double sum = 0; for (int i = 0; i < values.Length; i++) sum += values[i]; return (sum / values.Length); } public static void { // Use 'params' keyword. // Pass in a comma delimited list of doubles… double average; average = CalculateAverage(4.0, 3.2, 5.7); Console.WriteLine("Average of 4.0, 3.2, 5.7 is: {0}", average); // …or pass an array of doubles. double[] data = { 4.0, 3.2, 5.7 }; average = CalculateAverage(data); Console.WriteLine("Average of data is: {0}", average); Console.ReadLine(); }
3.12、迭代結構
- for迴圈
- foreach/in迴圈
- while和do/while迴圈
3.13、判斷結構與關係/相等運算子
- if/else語句
- switch語句
3.14、值型別和引用型別
值型別包括數值型別、列舉和結構,它們都分配在棧上,一旦離開定義的作用域,立即就會從記憶體中刪除。當一個值型別賦值給另一個值型別的時候,預設情況下完成的是一個成員到另一個成員的複製。就數值和布林型而言,唯一要複製的就是變數本身的值。
值型別都繼承自System.ValueType。功能上說,System.ValueTpe類唯一作用就是重寫由System.Object定義的虛方法,以遵守基於值而非引用的語義。
結構也是值型別,具有了在棧上分配資料效率的同時,又能發揮面向物件的最基本優點(封裝)。
結構型別也是用new建立,但是是在棧上分配。
結構的預設建構函式是保留的,不允許重定義。
MyPoint p = new MyPoint();
也可以不用new,但是要對每一個欄位賦值。
MyPoint p1;
p1.x = 100;
p1.y = 200;
1、值型別、引用型別和賦值運算子
值型別賦值的時候,是複製各個值到賦值目標,實際上各自在棧中都有存在,對一個的操作不會影響另一個。引用型別賦值時,將會產生一個對該堆上同一個物件的新引用。
示例程式碼見“2、包含引用型別的值型別”。
2、包含引用型別的值型別
當值型別包含其他引用引數時,賦值將生產一個“引用”的副本。這樣就有了兩個獨立的結構,每一個都包含指向記憶體中同一個物件的引用,稱為“淺複製”。如果想執行一個“深複製”,即將內部引用的狀態完全複製到一個新物件中時,需要實現ICloneable介面。 // Change struct to class to see the different behaviors. struct MyPoint { public int x, y; } // This type will be used // within a struct. class ShapeInfo { public string infoString; public ShapeInfo(string info) { infoString = info; } } // This stuct has members that // are value types and ref types. struct MyRectangle { public ShapeInfo rectInfo; // Ref type. public int top, left, bottom, right; public MyRectangle(string info) { rectInfo = new ShapeInfo(info); top = left = 10; bottom = right = 100; } } class ValRefClass { static void Main(string[] args) { // The 'new' keyword is optional when creating value types // using the default constructor, however you must assign // all field data before use. Console.WriteLine("***** Value Types / Reference Types *****"); // Still on the stack! MyPoint p = new MyPoint(); Console.WriteLine("-> Creating p1"); MyPoint p1 = new MyPoint(); p1.x = 100; p1.y = 100; Console.WriteLine("-> Assigning p2 to p1"); MyPoint p2 = p1; // Here is p1. Console.WriteLine("p1.x = {0}", p1.x);//100 Console.WriteLine("p1.y = {0}", p1.y);//100 // Here is p2. Console.WriteLine("p2.x = {0}", p2.x);//100 Console.WriteLine("p2.y = {0}", p2.y);//100 // Change p2.x. This will NOT change p1.x. Console.WriteLine("-> Changing p2.x to 900"); p2.x = 900; // Print again. Console.WriteLine("-> Here are the X values again..."); Console.WriteLine("p1.x = {0}", p1.x);//100。如果將MyPoint改成類型別,此處會顯示900。 Console.WriteLine("p2.x = {0}", p2.x);//900 Console.WriteLine(); // Create the first MyRectangle. Console.WriteLine("-> Creating r1"); MyRectangle r1 = new MyRectangle("This is my first rect"); // Now assign a new MyRectangle to r1. Console.WriteLine("-> Assigning r2 to r1"); MyRectangle r2; r2 = r1; // Change values of r2. Console.WriteLine("-> Changing all values of r2"); r2.rectInfo.infoString = "This is new info!"; r2.bottom = 4444; // Print values Console.WriteLine("-> Values after change:"); Console.WriteLine("-> r1.rectInfo.infoString: {0}", r1.rectInfo.infoString);//This is new info! Console.WriteLine("-> r2.rectInfo.infoString: {0}", r2.rectInfo.infoString);//This is new info! Console.WriteLine("-> r1.bottom: {0}", r1.bottom);//100 Console.WriteLine("-> r2.bottom: {0}", r2.bottom);//4444 Console.ReadLine(); } }
3、按值傳遞引用型別
傳遞的複製的一份指向呼叫者物件的引用。因此可以改變物件的域資料,但是重新賦值對呼叫者不可見。示例程式碼請見“4、按引用傳遞引用型別”。
4、按引用傳遞引用型別
完全是對同一個物件操作,被呼叫者可以改變物件的狀態資料的值和所引用的物件。可重新賦值。 // Simple class to demo params keyword. class Person { public string fullName; public int age; public Person(string n, int a) { fullName = n; age = a; } public void PrintInfo() { Console.WriteLine("{0} is {1} years old", fullName, age); } } class Program { public static void ArrayOfObjects(params object[] list) { for (int i = 0; i < list.Length; i++) { if (list[i] is Person) { ((Person)list[i]).PrintInfo(); } else Console.WriteLine(list[i]); } Console.WriteLine(); } public static void SendAPersonByValue(Person p) { // Change some data of 'p'. p.age = 99; // This will be forgotten after the call! p = new Person("Nikki", 999); } public static void SendAPersonByReference(ref Person p) { // Change some data of 'p'. p.age = 555; // 'p' is now reassigned! p = new Person("Nikki", 999); } public static void Main() { // Passing ref-types by value. Console.WriteLine("***** Passing Person object by value *****"); Person fred = new Person("Fred", 12); Console.WriteLine("Before by value call, Person is:"); fred.PrintInfo(); SendAPersonByValue(fred); Console.WriteLine("After by value call, Person is:"); fred.PrintInfo();//年齡的更改會有效果,但是重新賦值不會起效。 // Passing ref-types by ref. Console.WriteLine("/n***** Passing Person object by reference *****"); Person mel = new Person("Mel", 23); Console.WriteLine("Before by ref call, Person is:"); mel.PrintInfo(); SendAPersonByReference(ref mel); Console.WriteLine("After by ref call, Person is:"); mel.PrintInfo();//被重新賦予另外一個物件 Console.ReadLine(); } }5、一些細節
表3.5 值型別和引用型別的比較問題 |
值型別 |
應用型別 |
該型別分配在哪裡? |
棧上 |
託管堆上 |
變數是如何表示的? |
值型別變數是區域性複製 |
引用型別變數指向被分配的例項所佔用的記憶體 |
基型別是什麼? |
System.ValueType |
除System.ValueType之外的任何型別,只要那個型別不是密封的 |
該型別能作為其他型別的基類嗎? |
不能。值型別總是密封的 |
是的。如果該型別不是密封的話 |
預設的引數傳遞行為是什麼? |
變數是按值傳遞的,一個變數的副本被傳入被呼叫的函式 |
變數按引用傳遞的,變數的地址傳入被呼叫的引數 |
該型別能重寫System.Object.Finalize()嗎? | 不能。值型別不會放在堆上,因此不需要被終結 |
可以間接的重寫 |
可以定義建構函式嗎? |
是的。但是預設的建構函式要被保留 |
當然 |
該型別的變數什麼時候消亡? |
當它們越出定義的作用域時 |
當託管堆被垃圾回收時 |
3.15、裝箱與拆箱操作
裝箱:顯式的通過在System.Object中儲存變數來將值型別轉換成對應的引用型別的過程。當裝箱一個值時,CLR在堆上分配一個新的物件,並將這個值型別的值複製到那個例項中。返回的是一個新分配物件的引用。拆箱:將物件引用所儲存的值轉換成對應的棧上的值型別的過程。要先驗證接受的資料型別與裝箱的型別是否相同。
拆箱到一個合適的資料型別是強制的。
1、裝箱和拆箱的用處
事實上很少需要手工的裝箱和拆箱。大多數的時候,C#的編譯器會在適當的時候自動裝箱變數。例如傳遞引數的時候。自動裝箱也發生在.NET基類庫中的型別的時候。例如System.Collections名稱空間中的一些類型別。
裝箱和拆箱會花費一定的時間。效能損失可以通過使用泛型來補償。
2、拆箱自定義的值型別
也就是結構體和列舉的時候,先通過is關鍵字判斷下是否為某種指定的型別。3.16、使用.NET列舉
預設時計數方案將第一個元素設定為0,以後依次遞增。可以改變第一個值,以下的依次遞增。也可以指定全部的或部分的值,指定時並不必遵循有序的原則,只是在未指定的那裡才將它的值設定成上一個的值加1。預設列舉每一個專案的儲存型別對映到System.Int32。可以改變這個設定。例如:
enmu EmpType : byte
{
Manager = 10;
Grunt = 1;
Contractor = 100;
VP = 9;
}
可以用列舉來代替“魔數”。
一個列舉的值必須總是帶著含有字首的列舉名稱被應用,例如EmpType.Grunt而不是Grunt。
- System.Emu基類
表3.6 部分System.Enum靜態成員
成員 |
作用 |
Format() |
根據指定的格式將指定的列舉型別的值轉換成和它等價的字串表示 |
GetName()/GetNames() |
在指定的、具有指定值的列舉中獲取常量名稱(或一個包含全部名稱的陣列) |
GetUnderlyingType() |
返回用來儲存給定列舉值的底層資料型別 |
GetValues() |
在指定的列舉中獲取常量值的陣列 |
IsDefined() |
返回一個值指示值的常量是否存在於指定的列舉中 |
Parse() |
將一個或多個列舉常量的名稱或數值的字串表示轉換成一個等價的列舉物件,返回的是System.Object,因此需要強制轉換。 |
C#列舉支援各種運算子的使用。 // Here is a custom enum. enum EmpType : byte { Manager = 10, Grunt = 1, Contractor = 100, VP = 9 } class Program { #region Helper function // Enums as parameters. public static void AskForBonus(EmpType e) { switch (e) { case EmpType.Contractor: Console.WriteLine("You already get enough cash..."); break; case EmpType.Grunt: Console.WriteLine("You have got to be kidding..."); break; case EmpType.Manager: Console.WriteLine("How about stock options instead?"); break; case EmpType.VP: Console.WriteLine("VERY GOOD, Sir!"); break; default: break; } } #endregion static void Main(string[] args) { Console.WriteLine("***** Enums as parameters *****"); EmpType fred; fred = EmpType.VP; AskForBonus(fred); // Print out string version of ‘fred’. Console.WriteLine("/n***** ToString() *****"); Console.WriteLine(fred.ToString()); //Get underlying type. Console.WriteLine("/n***** Enum.GetUnderlyingType() *****"); Console.WriteLine(Enum.GetUnderlyingType(typeof(EmpType))); // Get Fred's type, hex and value. Console.WriteLine("/n***** Enum.Format() *****"); Console.WriteLine("You are a {0}", fred.ToString()); Console.WriteLine("Hex value is {0}", Enum.Format(typeof(EmpType), fred, "x")); Console.WriteLine("Int value is {0}", Enum.Format(typeof(EmpType), fred, "D")); // Parse. Console.WriteLine("/n***** Enum.Parse() *****"); EmpType sally = (EmpType)Enum.Parse(typeof(EmpType), "Manager"); Console.WriteLine("Sally is a {0}", sally.ToString()); // Get all stats for EmpType. Console.WriteLine("/n***** Enum.GetValues() *****"); Array obj = Enum.GetValues(typeof(EmpType)); Console.WriteLine("This enum has {0} members:", obj.Length); // Now show the string name and associated value. foreach (EmpType e in obj) { Console.Write("String name: {0}", Enum.Format(typeof(EmpType), e, "G")); Console.Write(" ({0})", Enum.Format(typeof(EmpType), e, "D")); Console.Write(" hex: {0}/n", Enum.Format(typeof(EmpType), e, "X")); } // Does EmpType have a SalePerson value? Console.WriteLine("/n***** Enum.IsDefined() *****"); if (Enum.IsDefined(typeof(EmpType), "SalesPerson")) Console.WriteLine("Yep, we have sales people."); else Console.WriteLine("No, we have no profits...."); Console.WriteLine("/n***** < and > *****"); EmpType Joe = EmpType.VP; EmpType Fran = EmpType.Grunt; if (Joe < Fran) Console.WriteLine("Joe's value is less than Fran's value."); else Console.WriteLine("Fran's value is less than Joe's value."); Console.ReadLine(); } }
3.17、最重要的類:System.Object
當定義一個不顯式指定其基類的類時,它隱含著繼承自System.Object。System.Object定義了一組例項級別和類級別(靜態)成員。其中一些例項級別的成員是用virtual關鍵字宣告的,因此可以被派生類重寫。
表3.7 System.Object的核心成員
Object類的例項方法 |
作用 |
Equals() |
預設情況下,這個方法僅當被比較的項是記憶體中的同一個項時才返回true。因此,該方法用來比較物件的引用,而不是物件的狀態。典型情況下,這個方法重寫為僅當被比較的物件擁有相同的內部狀態值時返回ture(基於值的語義)。 注意:如果重寫了Equals(),也應該重寫GetHashCode()。 |
GetHashCode() |
這個方法返回一個能夠標識記憶體中指定物件的整數(hash值)。 如果你打算將自定的型別包含進System.Collections.Hashtable型別中,強烈建議您重寫這個成員的預設實現。 |
GetType() |
這個方法返回一個全面描述當前項細節的System.Type物件。簡而言之,這是一個對所有物件都可用的執行時型別資訊(RTTI)方法。 |
ToString() |
這個方法以那麼生怕測。typename的格式(也就是完全限定名)返回一個給定物件的字串表示。如果該型別不是定義在一個名稱空間中,只返回typename。該方法也可以被子類重寫,返回表示物件內部狀態的“名稱/值”對的標記化字串,而不是物件的完全像定名。 |
Finalize() |
暫時可以將這個受保護的方法理解為,當一個物件從堆中被刪除的時候由.NET執行庫呼叫。 |
MemberwiseClone() |
這個受保護的方法返回一個新的物件,它是當前物件的逐個成員的副本。因此,如果你的物件包含到其他物件的引用,那麼到這些型別的引用將被複制(也就是,它實現了淺複製)。如果物件包含值型別,得到的是值的完全副本。 |
3.18、重寫System.Object的一些預設行為
示例程式碼省略。System.Object定義了兩個靜態成員Object.Equals()和Object.ReferenceEquals()來測試基於值和基於引用的相等性。
示例程式碼待加
3.19、系統資料型別(和C#簡化符號)
內建的C#資料型別實際上是一種簡化符號,用來定義System名稱空間中已定義的型別。表3.8 系統型別和C#簡化符號
C#簡化符號 |
判斷符合CLS |
系統型別 |
範圍 |
作用 |
sbyte |
否 |
System.SByte |
-128~127 |
帶符號的8位數 |
byte |
是 |
System.Byte | 0~255 |
無符號的8位數 |
short |
是 |
System.Int16 | -32 768~32 767 |
帶符號的16位數 |
ushort |
否 |
System.UInt16 | 0~65535 | 無符號的16位數 |
int |
是 |
System.Int32 | -2 147 483 648~2 147 483 647 |
帶符號的32位數 |
uint |
否 |
System.UInt32 | 0~4 294 967 295 |
無符號的32位數 |
long |
是 |
System.Int64 | -9 223 372 036 854 775 808~ 9 223 372 036 854 775 807 |
帶符號的64位數 |
ulong |
否 |
System.UInt64 | 0~18 446 744 073 709 551 615 |
無符號的64位數 |
char |
是 |
System.Char | U0000~Uffff | 一個16位的unicode字元 |
float |
是 |
System.Single | 1.5×10-45~3.4×1038 | 32位浮點數 |
double |
是 |
System.Double | 5.0×10-324~1.7×10308 | 64位浮點數 |
bool |
是 |
System.Boolean | true或false |
表示布林值 |
decimal |
是 |
System.Decimal | 100~1028 | 96位帶符號數 |
string |
是 |
System.String | 受系統記憶體限制 |
表示一個unicode字元集合 |
object |
是 |
System.Object | 任何型別都可以儲存在一個object變數中 |
.NET世界中所有型別的基類 |
預設情況下,賦值運算子右邊的一個實數值字面量被當作double型別.因此,為了初始化一個浮點變數,使用字尾f或F.
由於它們都是繼承自System.Object,因此諸如GetHashCode等都可使用。 由於所有值型別都提供了一個預設的建構函式,因此使用new關鍵字建立一個系統型別是允許的,它將變數的值設定為預設值。 bool b1 = new bool(); //b1 = false bool b2 = false; .NET數值型別支援MaxValue和MinValue。可能還有其他更有用的成員,例如System.Double有Epsilon、PositiveInfinity、NegativeInfinity等成員。
1、Boolean、Char、DateTime和TimeSpan型別
System.Boolean不支援MaxValue和MinValue屬性,但是支援TrueString和FalseString屬性。System.Char和String型別一樣,也是基於unicode的。 .NET資料型別有這樣一種能力,通過給定文字生成(解析)相應的底層型別的變數。 bool myBool = bool.Parse("True"); System名稱空間提定義了一些很有用的資料型別,但沒有對應的C#關鍵字,具體說就是DateTime和TimeSpan結構。
3.20、System.String資料型別
一些基本的操作:Length、Contains()、Format()、Insert()、PadLeft()、PadRight()、Remove()、Replace()、SubString()、ToCharArray()、ToUpper()、ToLower()。 儘管string是一個引用型別,但是相等性運算子(==和!=)被定義為比較字串物件的值而不是它們所引用的記憶體。 string型別可以使用類似陣列的語法遍歷每一個字元,就是其內容支援陣列式訪問的物件使用了索引器方法。 也可以使用foreach來遍歷,因為System.String維護著一個System.Char型別的陣列。如下: foreach(char c in sl) Console.WriteLine(c);1、轉義字元
支援同C語言基本一致的轉義字元,如下:/'、/"、//、/a、/n、/r、/t。 C#引入了以@為字首的字串字面量記法,稱為“逐字字串”。例如: Console.WriteLine(@"C:/MyApp/bin/debug"); 還可以容國重複“標記向一個字面量字串插入一個雙引號”,例如: Console.WriteLine(@"Cerbus said ""Darrr!""");3.21、System.Text.StringBuilder的作用
由於string一旦建立,它的值就不能再修改,因此造成處理大文字資料時效率的低下,而StringBuilder提供了對底層緩衝區的直接訪問,因此可以適應。預設StringBuilder的容量是16。 在很多情況下,應該用System.String來表示文字,但是如果是一個文字密集的應用程式,應該用StingBuilder。3.22、.NET陣列型別
資料是引用型別,繼承自System.Array的公共基類,預設情況下,陣列以0為下界,但是也可以使用靜態的System.Array.CreateInstance()方法來建立一個帶有任意下界的陣列。 如果建立一個值在以後才被確定的陣列,應在它分配的時候使用方括號指定陣列的大小。例如: string[] booksOnCOM; booksOnCOM = new string[3]; 以上也可以用一行程式碼來表示: string[] booksOnCOM = new string[3]; 如果在宣告時就知道陣列的值,可以在大括號中指定這些值。這種情況下,陣列的大小和new關鍵字都是可選的。例如: int[] n = new int[] {1, 2, 3 4}; int[] n3 = {1, 2, 3, 4}; 最後還有一種方式如下: int[] n2 = new int[4] {1, 2, 3, 4}; 這種情況下,指定的數值代表陣列中元素的個數,而不是上界的值。如果宣告的大小和初始化的個數不一致將會導致編譯錯誤。 不管怎樣宣告一個數組,都要知道.NET陣列中的元素會自動設定為它們各自的預設值。1、陣列作為引數和返回值
陣列可以作為引數傳遞,也可以當作成員返回值接收。2、多維陣列
第一種為矩陣陣列,例如: int[,] myMatrix; myMartix = new int[6,6]; 第二種稱為交錯陣列,它由一些內部的陣列組成,其中的每一組都可能有不同的上界。 int[][] myJagArray = new int[5]][]; for(int i = 0; i < myJaqArray.Length; i++) myJaqArray[i] = new int[i+7];3、 System.Array基類
表3.9 部分System.Array成員成員 | 作用 |
BinarySearch() | 這個靜態方法在(已經排序的)陣列中搜索指定的項。如果這個陣列是由你建立的自定義型別組成的,該型別必須實現IComparer接口才能進行二分搜尋。 |
Clear() | 這個靜態方法將陣列中一個範圍內的元素設定為空值(值型別為0,引用型別為null)。 |
CopyTo() | 用來從源陣列向目標陣列複製元素。 |
Length | 只讀屬性用來確定一個數組中元素的數目。 |
Rank | 該屬性返回陣列的維數。 |
Reverse() | 這個靜態方法反轉一個一維陣列的內容。 |
Sort() | 這個方法給一個內建型別的一維陣列排序。如果陣列中的元素實現了IComparer介面,也可以給自定義型別排序。 |