C#基礎知識系列三(類和結構體、String和StringBuilder、equals和==)

weixin_34292959發表於2014-01-08

前言

   這一節主要來了解一下類和結構體之間的異同點、以及針對String和StringBuilder的用法、equals和==,其實可以看出很多地方都用到了上一節的值型別和引用型別、堆疊和裝箱拆箱操作吧,慢慢的應用於實踐,讓理論與實踐結合起來。

類和結構體

類和結構體的不同點: 

  1.關鍵字不同 一個是class,一個是struct

  2.型別不同,一個是引用型別,一個是值型別(儲存:一個堆區,一個棧區)。關於值型別和引用型別以及堆與棧詳細可見http://www.cnblogs.com/aehyok/p/3504449.html

  3.成員不同,結構體沒有預設的建構函式(可以新增)和沒有解構函式,不可以使用abstract,protected,sealed修飾

  4.Struct變數使用完之後就自動解除記憶體分配,Class例項有垃圾回收機制來保證記憶體的回收處理

  5.繼承性。結構不可以繼承自另一個結構或被繼承,但和類一樣可以繼承自介面

  6.在結構體中可以宣告欄位,但是宣告欄位的時候是不能給初始值的.

  7.實體類中如果我們沒有顯示的定義建構函式,那麼會有一個隱式無參的建構函式(過載建構函式之後,需要顯示宣告無參建構函式),

    而在結構體中隱身無參的建構函式無論如何都存在

  8.在類中可以顯示的定義無參的建構函式,而在結構體中我們不能顯示的定義無參的建構函式

  9.結構體是可以New的,而結構體建構函式要求必須要為所有的欄位賦值.即使是無參的建構函式,也會給值型別賦初值為0,引用型別賦初值為null

同:

  1.都有屬性和方法

  2.和類一樣可以繼承自介面

String和StringBuilder

  String 物件是不可改變的。每次使用 System.String 類中的方法之一時,都要在記憶體中建立一個新的字串物件,這就需要為該新物件分配新的空間。在需要對字串執行重複修改的情況下,與建立新的 String 物件相關的系統開銷可能會非常昂貴。如果要修改字串而不建立新的物件,則可以使用 System.Text.StringBuilder 類。例如,當在一個迴圈中將許多字串連線在一起時,使用 StringBuilder 類可以提升效能。

 StringBuilder    MyStringBuilder    =    new    StringBuilder("Hello    World!");   

  通過用一個過載的建構函式方法初始化變數,可以建立 StringBuilder 類的新例項,正如以下示例中所闡釋的那樣。

  設定容量和長度 
  雖然 StringBuilder 物件是動態物件,允許擴充它所封裝的字串中字元的數量,但是您可以為它可容納的最大字元數指定一個值。此值稱為該物件的容量,不應將它與當前 StringBuilder 物件容納的字串長度混淆在一起。例如,可以建立 StringBuilder 類的帶有字串“Hello”(長度為 5)的一個新例項,同時可以指定該物件的最大容量為 25。當修改 StringBuilder 時,在達到容量之前,它不會為其自己重新分配空間。當達到容量時,將自動分配新的空間且容量翻倍。可以使用過載的建構函式之一來指定 StringBuilder 類的容量。以下程式碼示例指定可以將 MyStringBuilder 物件擴充到最大 25 個空白。   

StringBuilder    MyStringBuilder    =    new    StringBuilder("Hello    World!",    25);   

另外,可以使用讀/寫    Capacity    屬性來設定物件的最大長度。以下程式碼示例使用    Capacity    屬性來定義物件的最大長度。

MyStringBuilder.Capacity    =    25;   

EnsureCapacity 方法可用來檢查當前 StringBuilder 的容量。如果容量大於傳遞的值,則不進行任何更改;但是,如果容量小於傳遞的值,則會更改當前的容量以使其與傳遞的值匹配。

  也可以檢視或設定 Length 屬性。如果將 Length 屬性設定為大於 Capacity 屬性的值,則自動將 Capacity 屬性更改為與 Length 屬性相同的值。如果將 Length 屬性設定為小於當前 StringBuilder 物件內的字串長度的值,則會縮短該字串。  

這裡有篇關於站長大神的博文:使用string.Format需要注意的一個效能問題http://www.cnblogs.com/dudu/archive/2012/05/29/string_format_stringbuilder.html

StringBuilder,String.concat(),String+String 哪一個效率高?http://q.cnblogs.com/q/36917/

equals和==

對於值型別,如果物件的值相等,則相等運算子 (==) 返回 true,否則返回 false。

對於string 以外的引用型別,如果兩個物件引用同一個物件,則 == 返回 true。對於 string 型別,== 比較字串的值。

 ==操作比較的是兩個變數的值是否相等。

 equals()方法比較的是兩個物件的內容是否一致,equals也就是比較引用型別是否是對同一個物件的引用。

對於值型別的比較簡單,在此我們主要來看引用型別:

    public class Person
    {
        public string Name { get; set; }
        public Person(string name)
        {
            this.Name = name;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {

            string a = new string(new char[] { 'h', 'e', 'l', 'l', 'o' });
            string b = new string(new char[] { 'h', 'e', 'l', 'l', 'o' });
            Console.WriteLine(a == b);
            Console.WriteLine(a.Equals(b));

            object g = a;
            object h = b;
            Console.WriteLine(g == h);
            Console.WriteLine(g.Equals(h));

            Person p1 = new Person("aehyok");
            Person p2 = new Person("aehyok");
            Console.WriteLine(p1 == p2);
            Console.WriteLine(p1.Equals(p2));


            Person p3 = new Person("aehyok");
            Person p4 = p3;
            Console.WriteLine(p3 == p4);
            Console.WriteLine(p3.Equals(p4));
            Console.ReadLine();
        }
    }

結果輸出:

因為值型別是儲存在記憶體中的堆疊(以後簡稱棧),而引用型別的變數在棧中僅僅是儲存引用型別變數的地址,而其本身則儲存在堆中。

==操作比較的是兩個變數的值是否相等,對於引用型變數表示的是兩個變數在堆中儲存的地址是否相同,即棧中的內容是否相同。
equals操作表示的兩個變數是否是對同一個物件的引用,即堆中的內容是否相同。

而字串是一個特殊的引用型型別,在C#語言中,過載了string 物件的很多方法方法(包括equals()方法),使string物件用起來就像是值型別一樣。
因此在上面的例子中,字串a和字串b的兩個比較是相等的。

而g.equals(h)用的是sting的equals()方法故相等(多型)。如果將字串a和b作這樣的修改:
        string a="aa";
        string b="aa";
則,g和h的兩個比較都是相等的。這是因為系統並沒有給字串b分配記憶體,只是將"aa"指向了b。所以a和b指向的是同一個字串(字串在這種賦值的情況下做了記憶體的優化)。

對於p1和p2,也是記憶體中兩個不同的物件,所以在記憶體中的地址肯定不相同,故p1==p2會返回false,又因為p1和p2又是對不同物件的引用,所以p1.equals(p2)將返回false。
對於p3和p4,p4=p3,p3將對物件的引用賦給了p4,p3和p4是對同一個物件的引用,所以兩個比較都返回true。

相關文章