C#基礎知識之類和結構

雖然項目中一直在使用類、結構體等類型,仔細琢磨,還真沒法系統的說出個因此然。記錄一下類、結構體、類和結構體區別html

1、類程序員

  對於類,你們都特別熟悉。簡單的介紹一下類的結構,而後記錄一下Class須要注意的地方。考慮到靜態類和非靜態類的區別。下面介紹的都是主要以非靜態類爲前提介紹。  數組

  一、類的成員網絡

    類的數據和函數都屬於類的成員   數據結構

    

 

    (1)類的成員能夠分爲兩類:首先是類自己所聲明的。而後是從基類中繼承來的。若是在類聲明中沒有指定基類,則該類將繼承System.Object類的全部成員。less

    (2)C#中的類成員能夠是任意類型,包括數組和集合。ide

    (3)C#類成員修飾符。函數

      private聲明私有成員,只有該類中的成員能夠訪問,若是在聲明中沒有設置訪問修飾符,則默認是private。  性能

      public 聲明公有成員,訪問不受限制,容許從外部訪問。ui

      protected聲明受保護成員,包含類和包含類派生的類能夠訪問 ,對外界是隱藏的。

      internal聲明內部成員,只能當前程序集能夠訪問。

      protected internal聲明受保護的內部成員,只能訪問當前程序集和包含類派生的類。     

public class Teacher { private int _age; private string _name; public Teacher(int age, string name) { this.Age = age; this.Name = name ?? throw new ArgumentNullException(nameof(name)); } public int Age { get { return this._age; } set { this._age = value;} } public string Name { get { return this._name; } set { this._name = value; } } public string GetName() { return this.Name; } }
View Code

 

  二、類的構造函數

    概念:構造函數是類的一種特殊方法,每次建立類的實例都會調用它。在建立一個類的實例時,構造函數就像一個方法同樣被調用,但不返回值。

    分類:實例構造函數,靜態構造函數,私有構造函數。

    (1)實例構造函數的特徵           

      a、構造函數的名字與類名相同。

      b、使用 new 表達式建立類的對象或者結構(例如int)時,會調用其構造函數。而且一般初始化新對象的數據成員。

      c、除非類是靜態的,不然會爲沒有構造函數的類,自動生成一個默認構造函數,並使用默認值來初始化對象字段。

      d、構造函數能夠有參數,能夠以重載的形式存在多個構造函數。      

       

class Program { static void Main(string[] args) { Car car = new Car(); car.Print(); Car car1 = new Car(200000,"奧迪"); car1.Print(); Console.ReadKey(); } } public class Car { private int _price; private string _carName; public Car() { this.Price = 600000; this.CarName = "奔馳"; } public Car(int price,string carName) { this.Price = price; this.CarName = carName; } public int Price { get { return this._price; } set { this._price = value; } } public string CarName { get { return this._carName; } set { this._carName = value; } } public void Print() { Console.WriteLine("{0}價格是{1}元!",this.CarName,this.Price); } }
View Code

 

    (2)靜態構造函數的特徵

      a、靜態構造函數不使用訪問修飾符或不能有參數。

      b、不能直接調用靜態構造函數。

      c、若是靜態構造函數引起異常,運行時將不會再次調用該函數,而且類型在程序運行所在的應用程序域的生存期內將保持未初始化。

      d、用戶沒法控制在程序中執行靜態構造函數的時間。

     

class Program { private static int _price=1000; public Program() { _price = 10; } static Program() { _price += 1; } static void Main(string[] args) { Console.WriteLine("房子價格{0}",_price);//程序啓動,靜態類就會佔用內存,會先執行靜態構造函數 _price=1001
            Program program = new Program(); Console.WriteLine("房子價格{0}",_price);//實例化類,會執行實例構造函數 _price=10
 Console.ReadKey(); } }      
View Code

 

    在調用某類的靜態函數時真正的執行順序:

    一、靜態變量 > 靜態構造函數 > 靜態函數

    二、靜態變量 > 靜態構造函數 > 構造函數

     

public class A { public static readonly int x; static A() { //第二步,調用B.y,此處B.y = 0,由於int類型在初始化階段,會給賦默認值,默認值爲0。最後x = 0 + 1(返回給第一步)
        x = B.y + 1; } } public class B { //第一步,調用A.x,而後執行類A的靜態構造函數,等待返回(第二步返回的A.x = 1,全部y = 1 + 1)
    public static int y = A.x + 1; public static void Main(string[] args) { //第三步,A.x = 1,y = 2。
        Console.WriteLine("x:{0},y:{1}。", A.x, y); Console.ReadLine(); } }
View Code

 

    (3)私有構造函數  

      私有構造函數是一種特殊的實例構造函數。 它一般用於只包含靜態成員的類中。 若是類具備一個或多個私有構造函數而沒有公共構造函數,則其餘類(除嵌套類外)沒法建立該類的實例。聲明空構造函數可阻止自動生成默認構造函數。 請注意,若是不對構造函數使用訪問修飾符,則在默認狀況下它仍爲私有構造函數。 可是,一般會顯式地使用 private 修飾符來清楚地代表該類不能被實例化。   其中單例模式就用到了私有構造函數的特性來保證類不會被實例化。

      

class Program { private Program() { // House house = new House();//註釋打開會報錯,錯誤信息:不可訪問,由於它受保護級別限制。由於私有構造函數沒法在類的外面實例化。
 } public class House { private int price; private House() { price = 1000; } static void Main(string[] args) { Program program = new Program();//嵌套類容許實例化
                House house = new House(); Console.WriteLine("price{0}",house.price);//輸出1000
 Console.ReadKey(); } } }
View Code

 

 

  三、類的析構函數

    (1)概念:析構函數(destructor) 與構造函數相反,當對象脫離其做用域時(例如對象所在的函數已調用完畢),系統自動執行析構函數。析構函數每每用來作「清理善後」 的工做(例如在創建對象時用new開闢了一片內存空間,應在退出前在析構函數中用delete釋放)。

    (2)特徵

      一個類只能有一個析構函數。

      不能在結構中定義析構函數。 只能對類使用析構函數。

      沒法繼承或重載析構函數。

      沒法調用析構函數。 它們是被自動調用的。

      析構函數既沒有修飾符,也沒有參數。

    (3)析構函數的做用

      程序員沒法控制什麼時候調用析構函數,由於這是由垃圾回收器決定的。 垃圾回收器檢查是否存在應用程序再也不使用的對象。 若是垃圾回收器認爲某個對象符合析構,則調用析構函數(若是有)並回收用來存儲此對象的內存。 程序退出時也會調用析構函數。

      一般,與運行時不進行垃圾回收的開發語言相比,C# 無需太多的內存管理。 這是由於 .NET Framework 垃圾回收器會隱式地管理對象的內存分配和釋放。 可是,當應用程序封裝窗口、文件和網絡鏈接這類非託管資源時,應當使用析構函數釋放這些資源。 當對象符合析構時,垃圾回收器將運行對象的 Finalize 方法

    (4)實例

      

class Program { static void Main(string[] args) { RedApple redApple = new RedApple(); } } class Fruits //基類Fruits
 { ~Fruits()//析構函數
 { Console.WriteLine("~Fruits()的析構函數"); } public Fruits() { Console.WriteLine("Fruits()的構造函數"); } } class Apple:Fruits { ~Apple() { Console.WriteLine("~Apple()的析構函數"); } public Apple() { Console.WriteLine("Apple()的構造函數"); } } class RedApple:Apple { ~RedApple() { Console.WriteLine("~RedApple()的析構函數"); } public RedApple() { Console.WriteLine("RedApple()的構造函數"); } }  
View Code

 

     運行結果      

     

 

      總結:

        1>程序運行時,這三個類的析構函數將自動被調用,調用順序是按照從派生程度最大的(~C())到派生程度最小的(~A())次序調用的,和構造函數的調用順序正好相反。則能夠得出,當用戶建立類的對象是調用構造函數,而當該對象已經調用完畢時,使用析構函數

        2>析構函數(destructor) 與構造函數相反,當對象脫離其做用域時(例如對象所在的函數已調用完畢),系統自動執行析構函數。

        3>析構函數每每用來作「清理善後」 的工做(例如在創建對象時用new開闢了一片內存空間,應在退出前在析構函數中用delete釋放).

        4>析構函數名也應與類名相同,只是在函數名前面加一個波浪符~,例如~A( ),以區別於構造函數。它不能帶任何參數,也沒有返回值(包括void類型)。只能有一個析構函數,不能重載。

        5>若是用戶沒有編寫析構函數,編譯系統會自動生成一個缺省的析構函數,它也不進行任何操做。因此許多簡單的類中沒有用顯式的析構函數。

  四、類的繼承

    (1)類的注意事項

      1>注意靜態類和非靜態類的特徵以及使用方式的不一樣。能夠參見C#基礎知識之靜態和非靜態

      2>派生類只能從一個類中繼承,從多個基類中派生一個類這每每會帶來許多問題,從而抵消了這種靈活性帶來的優點。

      3>C#中,派生類從它的直接基類中繼承成員:方法、域、屬性、事件、索引指示器。除了構造函數和析構函數,派生類隱式地繼承了直接基類的全部成員。

    (2)類的重寫 override和virtual

      1>重寫父類的方法要用到override關鍵字(具備override關鍵字修飾的方法是對父類中同名方法的新實現)。

      2>要重寫父類的方法,前提是父類中該要被重寫的方法必須聲明爲virtual或者是abstract類型。給父類中要被重寫的方法添加virtual關鍵字表示能夠在子類中重寫它的實現。

 

      3>virtual關鍵字用於將方法定義爲支持多態,有virtual關鍵字修飾的方法稱爲「虛擬方法」。 

      4>virtual:不是必須被子類重寫的方法,父類必須給出實現,子類能夠重寫(使用override,new,或無特殊標誌的普通方法),也能夠不重寫該方法。

         5>new:重寫父類方法時,父類可使用virtual,override,new之一聲明,也能夠是沒有關鍵字的普通方法,運行時會根據引用類型選擇調用父類仍是子類方法,重寫父類方法時,使用new關鍵字與使用沒有關鍵字的普通方法的等效的,可是後者會給出編譯警告。

    

class Program { static void Main(string[] args) { Dog dog = new Dog(""); dog.ShowName(); Console.ReadKey(); } } class Animal { private string _name; public string Name { get { return _name; } set { _name = value; } } public virtual void ShowName() { Console.WriteLine("動物列表:"); } } class Dog : Animal { public Dog(string name) { base.Name = name; } public override void ShowName() { base.ShowName(); Console.WriteLine("動物的名稱:{0}",base.Name); } } 
View Code

 

class Program { static void Main(string[] args) { Dog dog = new Dog(""); dog.ShowName(); Console.ReadKey(); } } abstract class Animal { private string _name; public string Name { get { return _name; } set { _name = value; } } public abstract void ShowName(); } class Dog : Animal { public Dog(string name) { base.Name = name; } public override void ShowName() { Console.WriteLine("動物的名稱:{0}",base.Name); } }
View Code

 

    (3)類的重載

      方法名相同,可是參數不一樣,參數的個數不一樣或者類型不一樣,知足一個就能夠(和返回值無關,和參數的類型和個數有關)。注意:返回值能夠相同也能夠不一樣。當參數個數相同而參數類型不一樣的時候,能夠考慮使用泛型(C#基礎知識之泛型),提升代碼的複用性。

      

class Dog { public void Show() { } public void Show(string name) { } public void Show(string name,int weight) { } public void Show(int weight,int age) { } }
View Code

 

 

    (4)抽象類

      修飾類名爲抽象類,修飾方法爲抽象方法。若是一個類爲抽象類,則這個類只能是其餘某個類的基類。抽象方法在抽象類中沒有函數體。抽象類中的抽象方法是沒有方法體的,繼承其的子類必須實現抽象類的抽象方法。

      1>抽象類的特徵        

        u  抽象類不能實例化。

        u  抽象類的派生類必須實現全部抽象方法。

        u  抽象類的抽象方法是沒有方法體的,繼承抽象類的子類必須得實現全部抽象方法。

      2>抽象方法的特徵

u  抽象方法是隱式的虛方法。

u  只容許在抽象類中聲明抽象方法。

u  抽象方法在抽象類中沒有方法體。

u  在抽象方法聲明中,不能使用static和virtual修飾符。

    (5)密封類

      密封類不能被繼承,因此sealed和abstract不能共用。

      1>密封類      

      

class Program { static void Main(string[] args) { B b = new B(); b.x = 10; b.y = 20; Console.WriteLine("b.x {0}, b.y {1}",b.x,b.y); Console.ReadKey(); } } public sealed class B { public int x; public int y; }
View Code

 

      2>密封方法      

        對類中的方法可使用sealed修飾符,咱們稱該方法爲密封方法。不是類的每一個成員方法均可以做爲密封方法,密封方法必須對基類的虛方法進行重載,提供具體的實現方法。因此,在方法的聲明中,sealed修飾符老是和override修飾符同時使用,sealed修飾符位於override修飾符前面。

   

class Program { static void Main(string[] args) { A a1 = new A();//實例化A類的對象,調用A類中的方法
 a1.Fun1(); a1.Fun2(); A a2 = new B();//實例化B類的對象,調用B類中的方法,注意前邊 new做爲修飾符的用法(很重要!)
 a2.Fun1(); a2.Fun2(); A a3 = new C();//實例化C類的對象,調用C類中的方法,由於FUN1()在B類中使用密封修飾符,因此C類中沒法進行重寫,因此調用B.Fun1()
 a3.Fun1(); a3.Fun2(); B b1 = new B();//實例化B類的對象,調用B類中的方法
 b1.Fun1(); b1.Fun2(); B b2 = new C();//實例化C類的對象,由於FUN1()在B類中使用密封修飾符,因此C類中沒法進行重寫,因此調用B.Fun1()
 b2.Fun1(); b2.Fun2(); C c1 = new C();//由於FUN1()在B類中使用密封修飾符,因此C類中沒法進行重寫,因此調用B.Fun1()
 c1.Fun1(); c1.Fun2(); Console.ReadKey(); } } public class A { public virtual void Fun1() { Console.WriteLine("base.Fun1();"); } public virtual void Fun2() { Console.WriteLine("base.Fun2();"); } } public class B:A { public sealed override void Fun1() { Console.WriteLine("B.Fun1();"); } public override void Fun2() { Console.WriteLine("B.Fun2();"); } } public class C : B { public override void Fun2() { Console.WriteLine("C.Fun2()"); } }  
View Code

 

 

    (6)New  new的關鍵詞有三個功能      

u  做爲運算符:     用於建立對象和調用構造函數。 Class obj = new Class();

u  做爲修飾符 :   在用做修飾符時,new 關鍵字能夠顯式隱藏從基類繼承的成員。具體地說,new聲明的方法,當使用子類的類型來調用的時候,它會運行子類的函數,而若是類型是基類的話,被隱藏的基類函數會被調用。而子類中函數使用override的時候,則當使用子類的類型來調用的是,它會運行子類的函數,類型是基類的時候,仍會調用子類函數。

    

class Program { static void Main(string[] args) { A a1 = new A();//若是類型是基類的話,被隱藏的基類函數會被調用
 a1.ShowInfo(); System.Console.WriteLine("----------"); A a2 = new B(); a2.ShowInfo();//若是類型是基類的話,被隱藏的基類函數會被調用
            System.Console.WriteLine("----------"); B a3 = new B(); a3.ShowInfo();//若是類型是子類的話,它會運行子類的函數
            System.Console.WriteLine("----------"); A a4 = new C();//子類中函數使用override的時候,類型是基類的時候,仍會調用子類函數。
 a4.ShowInfo(); System.Console.WriteLine("----------"); C a5 = new C(); a5.ShowInfo();//子類中函數使用override的時候,則當使用子類的類型來調用的是,它會運行子類的函數
            System.Console.WriteLine("----------"); Console.ReadKey(); } class A { public virtual void ShowInfo() { System.Console.WriteLine("Four wheels and an engine."); } } class B : A { public new void ShowInfo() { System.Console.WriteLine("A roof that opens up."); } } class C : A { public override void ShowInfo() { System.Console.WriteLine("Carries seven people."); } } }
View Code

 

      u  做爲約束:       用於在泛型聲明中約束可能用做類型參數的參數的類型。new約束指定泛型類聲明中的任何類型參數都必須具備公共的無參數構造函數

   

class Program { static void Main(string[] args) { AFactory<A> aFactory = new AFactory<A>(); //此處編譯器會檢查Employee是否具備公有的無參構造函數。 //若沒有則會有The Employee must have a public parameterless constructor 錯誤。
            Console.WriteLine("{0}'ID is {1}.", aFactory.GetName().Name, aFactory.GetName().ID); Console.ReadKey(); } } public class A { private string name; private int id; public A() { name = "dachong"; id = 100; } public A(string s, int i) { name = s; id = i; } public string Name { get { return name; } set { name = value; } } public int ID { get { return id; } set { id = value; } } } class AFactory<T> where T : new() { public T GetName() { return new T(); } }
View Code

 

 

2、結構體

  一、概念:C# 中,結構體是值類型數據結構。它使得一個單一變量能夠存儲各類數據類型的相關數據。struct 關鍵字用於建立結構體。結構體是用來表明一個記錄。

  二、特色:C#中結構體的特色

    (1)結構可帶有方法、字段、索引、屬性、運算符方法和事件。

    (2)結構可定義構造函數,但不能定義析構函數。可是,您不能爲結構定義默認的構造函數。默認的構造函數是自動定義的,且不能被改變。

    (3)與類不一樣,結構不能繼承其餘的結構或類。

    (4)結構可實現一個或多個接口。

    (5)結構成員不能指定爲 abstract、virtual 或 protected。

    (6)當您使用 New 操做符建立一個結構對象時,會調用適當的構造函數來建立結構。與類不一樣,結構能夠不使用 New 操做符便可被實例化。若是不使用 New 操做符,只有在全部的字段都被初始化以後,字段才被賦值,對象才被使用。

 

struct Books { private string title; private string author; private string subject; private int book_id; public void getValues(string t, string a, string s, int id) { title = t; author = a; subject = s; book_id =id; } public void display() { Console.WriteLine("Title : {0}", title); Console.WriteLine("Author : {0}", author); Console.WriteLine("Subject : {0}", subject); Console.WriteLine("Book_id :{0}", book_id); } }; class Program { Books Book1 = new Books(); Book1.getValues("C Programming", "Nuha Ali", "C Programming Tutorial",6495407); Book1.display(); Console.ReadKey(); } 
View Code

 

   

3、類和結構體區別   

  一、結構是值類型,它在棧中分配空間;而類是引用類型,它在堆中分配空間,棧中保存的只是引用。

  二、C# 中的簡單類型,如int、double、bool等都是結構類型。若是須要的話,甚至可使用結構類型結合運算符運算重載,再爲 C# 語言建立出一種新的值類型來。因爲結構是值類型,而且直接存儲數據,所以在一個對象的主要成員爲數據且數據量不大的狀況下,使用結構會帶來更好的性能。由於結構是值類型,所以在爲結構分配內存,或者當結構超出了做用域被刪除時,性能會很是好,由於他們將內聯或者保存在堆棧中。當把一個結構類型的變量賦值給另外一個結構時,對性能的影響取決於結構的大小,若是結構的數據成員很是多並且複雜,就會形成損失。

  三、在結構體中能夠聲明字段,可是聲明字段的時候是不能給初始值的。

  四、類中若是咱們沒有爲類寫任意的構造函數,那麼C#編譯器在編譯的時候會自動的爲這個類生成1個無參數的構造函數.咱們將這個構造函數稱之爲隱式構造函數 可是一旦咱們爲這個類寫了任意的1個構造函數的時候,這個隱式的構造函數就不會自動生成了。隱式的無參數的構造函數在結構中不管如何都是存在的,因此程序員不能手動的爲結構添加1個無參數的構造函數,不然會報錯。

  五、C#語法規定在結構體的構造函數中,必需要爲結構體的全部字段賦值。注意在結構體的構造函數中咱們爲屬性賦值,不認爲是在對字段賦值,因此咱們在構造函數中要直接爲字段賦值.

4、類和結構體的使用場所

  一、當堆棧的空間頗有限,且有大量的邏輯對象時,建立類要比建立結構好一些;

  二、對於點、矩形和顏色這樣的輕量對象,假如要聲明一個含有許多個顏色對象的數組,則CLR須要爲每一個對象分配內存,在這種狀況下,使用結構的成本較低;

  三、在表現抽象和多級別的對象層次時,類是最好的選擇,由於結構不支持繼承。

相關文章
相關標籤/搜索