1. 程式人生 > >《C#與.NET3.0高階程式設計》摘要-----第三章 C#語言基礎

《C#與.NET3.0高階程式設計》摘要-----第三章 C#語言基礎

第三章 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方法的其他形式

    可以帶引數或不帶,可以返回int或void。     Main()方法在vs2005裡自動定義成private的,其他程式集不能直接呼叫該應用程式的入口。     如何構造Main()取決於:是否需要處理使用者提供的命令列引數;完成時是否需要向系統返回一個值。

3、處理命令列引數

    可以直接訪問args,用for或foreach遍歷。     也可以通過System.Enviroment型別的靜態方法GetCommandLineArgs()來訪問,返回的是字串陣列,第一個索引表示包含應用程式本身的當前目錄,其餘的元素包含單獨的命令列引數。使用時,不用再將Main()定義成一個帶字串陣列引數的方法。     使用VS2005指定命令列引數:Solution Explore裡的Debug那裡指定Command line arguments。

3.2、System.Enviroment類

    包含很多靜態函式可以獲得作業系統的細節。例如:OsVersion, CurrentDirectory, GetLogicDrives, Version, MchineName, NewLine, ProcessorCount, SystemDirectory, UserName。

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,十六進位制格式也會包含大寫的字元。
    例如下面程式:     Console.WriteLine("{0}, Number{0}, Number{0}", 9);     Console.WriteLine("C format: {0:C}", 99989.987);     Console.WriteLine("D9 format: {0:D9}", 99999);     Console.WriteLine("E format: {0:E}", 99999.76543);     Console.WriteLine("F3 format: {0:F3}", 99999.9999);     Console.WriteLine("N format: {0:N}", 99999);     Console.WriteLine("X format: {0:X}", 99999);     Console.WriteLine("x format: {0:x}", 99999);     應該意識到.NET格式化字元的使用不僅僅侷限於控制檯應用程式,同樣能用在靜態和String.Format()方法的上下文中。例如:     string formatStr;     formatStr = String.Format("Don't you wish you had {0:C} in your account?", 99989.987);     Console.WriteLine(formatStr);

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語句
        只能使用bool型的表示式。
  • 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基類
        列舉都隱式的派生自System.Enum。該基類定義了很多靜態方法。
        表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介面,也可以給自定義型別排序。

3.23、可空型別

    CLR資料型別有個一確定的範圍,例如Boolean型只能從集合{true, false}中賦值,但是還可以建立可空型別,這樣就可以賦給null值,這樣就可以從集合{true, false, null}中賦值了。     為了定義一個可空型別,應在底層資料型別中新增問號符號作為字尾。例如:     int? nullableInt = 10;     值得注意的是:這種語法只對值型別或者值型別的陣列是合法的。     ??運算子:是C#2005特有的。這個運算子在獲得的值實際上是null時給一個可空型別賦值。例如:     int? myData = dr.GetIntFromDB() ?? 100;

3.24、定義自定義名稱空間

    略去