封裝是實現面向對象程序設計的第一步,封裝就是將數據或函數等集合在一個個的單元中(咱們稱之爲類)。被封裝的對象一般被稱爲抽象數據類型。html
面向對象程序設計中通常以類做爲數據封裝的基本單位。類將數據和操做數據的方法結合成一個單位。在設計類時,不但願直接存取類中的數據,而是但願經過方法來存取數據。如此就能夠達到封裝數據的目的,方便之後維護、升級,也能夠在操做數據時多一層判斷,提升安全性。sql
在C#中可以使用類來達到數據封裝的效果,這樣就可使數據與方法封裝成單一元素,以便於經過方法存取數據。除此以外,還能夠控制數據的存取方式。封裝在C#中可以使用類來達到數據封裝的效果,這樣就可使數據與方法封裝成單一元素,以便於經過方法存取數據。除此以外,還能夠控制數據的存取方式。數據庫
(一) 封裝的意義
封裝的意義在於保護或者防止代碼(數據)被咱們無心中破壞。在面向對象程序設計中數據被看做是一箇中心的元素而且和使用它的函數結合的很密切,從而保護它不被其它的函數意外的修改。編程
封裝還能夠解決數據存取權限問題,使用封裝能夠將數據隱藏起來,造成一個封閉的空間,用戶能夠設置哪些數據只能在這個空間中使用,哪些數據能夠在空間外部使用。若是一個類中包含敏感數據,則有些用戶能夠訪問,有些用戶卻不能訪問。若是不對這些數據的訪問加以限制,那麼後果是很嚴重的。因此,在編寫程序時,要對類的成員使用不一樣的訪問修飾符,從而定義它們的訪問級別。 封裝提供了一個有效的途徑來保護數據不被意外的破壞。相比咱們將數據(用域來實現)在程序中定義爲公用的(public)咱們將它們(fields)定義爲私有的(privat)在不少方面會更好。私有的數據能夠用兩種方式來間接的控制。第一種方法,咱們使用傳統的存、取方法。第二種方法咱們用屬性(property)。c#
使用屬性不只能夠控制存取數據的合法性,同時也提供了「讀寫」、「只讀」、「只寫」靈活的操做方法。數組
(二)訪問修飾符安全
一、Public 訪問修飾符ide
Public 訪問修飾符容許一個類將其成員變量和成員函數暴露給其餘的函數和對象。任何公有成員能夠被外部的類訪問。函數
下面的實例說明了這點:post
using System; namespace RectangleApplication { class Rectangle { //成員變量 public double length; public double width; public double GetArea() { return length * width; } public void Display() { Console.WriteLine("長度: {0}", length); Console.WriteLine("寬度: {0}", width); Console.WriteLine("面積: {0}", GetArea()); } }// Rectangle 結束 class ExecuteRectangle { static void Main(string[] args) { Rectangle r = new Rectangle(); r.length = 4.5; r.width = 3.5; r.Display(); Console.ReadLine(); } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
長度: 4.5 寬度: 3.5 面積: 15.75
在上面的實例中,成員變量 length 和 width 被聲明爲 public,因此它們能夠被函數 Main() 使用 Rectangle 類的實例 r 訪問。
成員函數 Display() 和 GetArea() 能夠直接訪問這些變量。
成員函數 Display() 也被聲明爲 public,因此它也能被 Main() 使用 Rectangle 類的實例 r 訪問。
二、Private 訪問修飾符
Private 訪問修飾符容許一個類將其成員變量和成員函數對其餘的函數和對象進行隱藏。只有同一個類中的函數能夠訪問它的私有成員。即便是類的實例也不能訪問它的私有成員。
下面的實例說明了這點:
using System; namespace RectangleApplication { class Rectangle { //成員變量 private double length; private double width; public void Acceptdetails() { Console.WriteLine("請輸入長度:"); length = Convert.ToDouble(Console.ReadLine()); Console.WriteLine("請輸入寬度:"); width = Convert.ToDouble(Console.ReadLine()); } public double GetArea() { return length * width; } public void Display() { Console.WriteLine("長度: {0}", length); Console.WriteLine("寬度: {0}", width); Console.WriteLine("面積: {0}", GetArea()); } }//end class Rectangle class ExecuteRectangle { static void Main(string[] args) { Rectangle r = new Rectangle(); r.Acceptdetails(); r.Display(); Console.ReadLine(); } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
請輸入長度: 4.4 請輸入寬度: 3.3 長度: 4.4 寬度: 3.3 面積: 14.52
在上面的實例中,成員變量 length 和 width 被聲明爲 private,因此它們不能被函數 Main() 訪問。成員函數 AcceptDetails() 和 Display() 能夠訪問這些變量。因爲成員函數 AcceptDetails() 和 Display() 被聲明爲 public,因此它們能夠被 Main() 使用 Rectangle 類的實例 r 訪問。
三、Protected 訪問修飾符
Protected 訪問修飾符容許子類訪問它的基類的成員變量和成員函數。這樣有助於實現繼承。咱們將在繼承的章節詳細討論這個。更詳細地討論這個。
四、Internal 訪問修飾符
Internal 訪問說明符容許一個類將其成員變量和成員函數暴露給當前程序中的其餘函數和對象。換句話說,帶有 internal 訪問修飾符的任何成員能夠被定義在該成員所定義的應用程序內的任何類或方法訪問。
下面的實例說明了這點:
using System; namespace RectangleApplication { class Rectangle { //成員變量 internal double length; internal double width; double GetArea() { return length * width; } public void Display() { Console.WriteLine("長度: {0}", length); Console.WriteLine("寬度: {0}", width); Console.WriteLine("面積: {0}", GetArea()); } }//end class Rectangle class ExecuteRectangle { static void Main(string[] args) { Rectangle r = new Rectangle(); r.length = 4.5; r.width = 3.5; r.Display(); Console.ReadLine(); } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
長度: 4.5 寬度: 3.5 面積: 15.75
在上面的實例中,請注意成員函數 GetArea() 聲明的時候不帶有任何訪問修飾符。若是沒有指定訪問修飾符,則使用類成員的默認訪問修飾符,即爲 private。
五、Protected Internal 訪問修飾符
Protected Internal 訪問修飾符容許在本類,派生類或者包含該類的程序集中訪問。這也被用於實現繼承。
(三) 現實中的封裝:
咱們的房子就是一個類的實例,室內的裝飾與擺設只能被室內的居住者欣賞與使用,若是沒有四面牆的遮擋,室內的全部活動在外人面前一覽無遺。因爲有了封裝,房屋內的全部擺設均可以隨意地改變而不用影響他人。然而,若是沒有門窗,一個包裹得嚴嚴實實的黑箱子,即便它的空間再寬闊,也沒有實用價值。房屋的門窗,就是封裝對象暴露在外的屬性
和方法,專門供人進出,以及流通空氣、帶來陽光
(四) 封裝的例子:
舉一個咱們項目開發中常常用到的例子,對數據庫操做,封裝成一個經常使用的幫助類,咱們進行重複調用,下面爲其中封裝的一個方法
private DataTable Getdate(string sqlstr, SqlParameter parameter) { SqlConnection conn = new SqlConnection(str);(這個str是鏈接數據庫) SqlCommand comm = new SqlCommand(sqlstr,conn); if (parameter!=null) { comm.Parameters.AddWithValue(parameter.ParameterName, parameter.Value); } SqlDataAdapter adapter = new SqlDataAdapter(comm); DataTable dt = new DataTable(); adapter.Fill(dt); return dt; }
繼承是OOP最重要的特性之一。任何類均可以從另一個類繼承,即這個類擁有它所繼承類的全部成員。在OOP中,被繼承的類稱爲父類或基類。
C#提供了類的繼承機制,但C#只支持單繼承,不支持多重繼承,即在C#中一次只容許繼承一個類,不能同時繼承多個類。利用繼承機制,用戶能夠經過增長、修改或替換類中方法對這個類進行擴充,以適應不一樣的應用要求。利用繼承,程序開發人員能夠在已有類的基礎上構造新類。繼承使得類支持分類的概念。在平常生活中不少東西比較有條理,那是由於它們有着很好的層次分類。若是不用層次分類,則要對每一個對象定義其全部的性質。使用繼承後,每一個對象就能夠只定義本身的特殊性質。每一層的對象只需定義自己的性質,其餘性質能夠從上一層繼承下來。
在C#中,接口容許多繼承,能夠經過繼承多個接口來實現相似於C++中的多重繼承。在繼承一個基類時,成員的可訪問性是一個重要的問題。子類不能訪問基類的私有成員,可是能夠訪問其公共成員。子類和外部代碼均可以訪問公共成員。這就是說,只使用這兩個可訪問性,就可讓一個成員被基類和子類訪問,同時也能夠被外部的代碼訪問。
爲了解決這個問題,C# 還提供了第3種可訪問性:protected。只有派生類才能訪問protected成員,基類和外部代碼都不能訪問protected成員。
除了成員的保護級別外,用戶還能夠爲成員定義其繼承行爲。基類的成員能夠是虛擬的,成員能夠由繼承它的類重寫。子類能夠提供成員的其餘執行代碼。這種執行代碼不會刪除原來的代碼,仍能夠在類中訪問原來的代碼,但外部代碼不能訪問它們。若是沒有提供其餘執行方式,外部代碼就訪問基類中成員的執行代碼。
虛擬成員不能是私有成員,由於成員不能同時由子類重寫,也不能訪問它。基類還能夠定義爲抽象類。抽象類不能直接實例化,要使用抽象類就必須繼承這個類,而後再實例化。
繼承主要實現重用代碼,節省開發時間。
(一)、C#中的繼承符合下列規則
1.繼承是可傳遞的。若是C從B中派生,B又從A中派生,那麼C不只繼承了B中聲明的成員,一樣也繼承了A中的成員。Object類做爲全部類的基類。
2.派生類應當是對基類的擴展。派生類能夠添加新的成員,但不能除去已經繼承的成員的定義。
3.構造函數和析構函數不能被繼承。除此以外的其它成員,不論對它們定義了怎樣的訪問方式,都能被繼承。基類中成員的訪問方式只能決定派生類可否訪問它們。
4.派生類若是定義了與繼承而來的成員同名的新成員,就能夠覆蓋已繼承的成員。但這並不由於這派生類刪除了這些成員,只是不能再訪問這些成員。
5.類能夠定義虛文法、虛屬性以及虛索引指示器,它的派生類可以重載這些成員,從而實現類能夠展現出多態性。
(二)、new關鍵字
若是父類中聲明瞭一個沒有friend修飾的protected或public方法,子類中也聲明瞭同名的方法。則用new能夠隱藏父類中的方法。(不建議使用)
(三)、base關鍵字
base關鍵字用於從派生類中訪問基類的成員:
1.調用基類上已被其餘方法重寫的方法。
2.指定建立派生類實例時應調用的基類構造函數。
(四)基類和派生類
一個類能夠派生自多個類或接口,這意味着它能夠從多個基類或接口繼承數據和函數。
C# 中建立派生類的語法以下:
<acess-specifier> class <base_class> { ... } class <derived_class> : <base_class> { ... }
假設,有一個基類 Shape,它的派生類是 Rectangle:
using System; namespace InheritanceApplication { class Shape { public void setWidth(int w) { width = w; } public void setHeight(int h) { height = h; } protected int width; protected int height; } // 派生類 class Rectangle: Shape { public int getArea() { return (width * height); } } class RectangleTester { static void Main(string[] args) { Rectangle Rect = new Rectangle(); Rect.setWidth(5); Rect.setHeight(7); // 打印對象的面積 Console.WriteLine("總面積: {0}", Rect.getArea()); Console.ReadKey(); } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
總面積: 35
(五)基類的初始化
派生類繼承了基類的成員變量和成員方法。所以父類對象應在子類對象建立以前被建立。您能夠在成員初始化列表中進行父類的初始化。
下面的程序演示了這點:
using System; namespace RectangleApplication { class Rectangle { // 成員變量 protected double length; protected double width; public Rectangle(double l, double w) { length = l; width = w; } public double GetArea() { return length * width; } public void Display() { Console.WriteLine("長度: {0}", length); Console.WriteLine("寬度: {0}", width); Console.WriteLine("面積: {0}", GetArea()); } }//end class Rectangle class Tabletop : Rectangle { private double cost; public Tabletop(double l, double w) : base(l, w) { } public double GetCost() { double cost; cost = GetArea() * 70; return cost; } public void Display() { base.Display(); Console.WriteLine("成本: {0}", GetCost()); } } class ExecuteRectangle { static void Main(string[] args) { Tabletop t = new Tabletop(4.5, 7.5); t.Display(); Console.ReadLine(); } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
長度: 4.5
寬度: 7.5
面積: 33.75
成本: 2362.5
(六)C# 多重繼承
多重繼承指的是一個類別能夠同時從多於一個父類繼承行爲與特徵的功能。與單一繼承相對,單一繼承指一個類別只能夠繼承自一個父類。
C# 不支持多重繼承。可是,您可使用接口來實現多重繼承。下面的程序演示了這點:
using System; namespace InheritanceApplication { class Shape { public void setWidth(int w) { width = w; } public void setHeight(int h) { height = h; } protected int width; protected int height; } // 基類 PaintCost public interface PaintCost { int getCost(int area); } // 派生類 class Rectangle : Shape, PaintCost { public int getArea() { return (width * height); } public int getCost(int area) { return area * 70; } } class RectangleTester { static void Main(string[] args) { Rectangle Rect = new Rectangle(); int area; Rect.setWidth(5); Rect.setHeight(7); area = Rect.getArea(); // 打印對象的面積 Console.WriteLine("總面積: {0}", Rect.getArea()); Console.WriteLine("油漆總成本: ${0}" , Rect.getCost(area)); Console.ReadKey(); } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
總面積: 35
油漆總成本: $2450
(一)、多態定義
派生類的實例由基類的實例加上派生類新增的成員組成。派生類的引用指向整個類對象,包括基類部分。若是有一個派生類對象的引用,就能夠獲取對象基類部分的引用(強制類型轉換)。
同一種操做做用於不一樣的類的對象,不一樣的類的對象進行不一樣的執行,最後產生不一樣的執行結果。簡單理解:讓一種對象表現出來多種類型。
以下簡單的例子:
使用這種方式,派生類能夠訪問基類和派生類中的全部成員,可是基類只能訪問基類中的成員。這是咱們想要的結果嗎?
(二) 多態的實現方式(虛方法):
一、定義:
在基類中用virtual關鍵字聲明的方法叫作虛方法,在派生類中能夠經過override關鍵字進行重寫
二、使用場景:
1. 明肯定義了基類,對基類中的方法進行重寫時,能夠考慮使用虛方法。
2.派生類的方法和基類的方法有相同的簽名和返回類型。
3.覆寫和被覆寫的方法具備相同等級的可訪問性。
三、例子:
class Person { private string _name; public string Name { get { return _name; } set { _name = value; } } public Person(string name) { this.Name = name; } public virtual void SayHello() { Console.WriteLine("我是人類"); } } class Chinese:Person { public Chinese(string name) : base(name) { } public override void SayHello() { Console.WriteLine("你好,個人姓名是{0},我是中國人",this.Name); } } class Japanese:Person { public Japanese(string name) : base(name) { } public override void SayHello() { Console.WriteLine("你好,個人姓名是{0},我是日本人", this.Name); } } class Program { static void Main(string[] args) { Chinese c1 = new Chinese("小明"); Chinese c2 = new Chinese("小李"); Japanese j1 = new Japanese("井泉宜良"); Japanese j2 = new Japanese("龜田君"); Person[] Pers = { c1, c2, j1, j2 }; //Pers[0] = c1; //Pers[1] = c2; //Pers[2] = j1; //Pers[3] = j2; 上面數組的另一種寫法 for (int i = 0; i < Pers.Length; i++) { Pers[i].SayHello(); } //Person p = new Chinese("小明");//Japanese("bingbian"); //p.SayHello(); Console.ReadKey(); } }
四、加深拓展
(三) 多態的實現方式(抽象方法)
一、定義:
描述共性的類就是抽象類,抽象類中的方法用abstract關鍵字修飾。其中抽象類並不考慮其實現的具體方式。
二、成員:
抽象方法和非抽象方法;屬性;字段;構造函數;實例成員
三、使用場景:
1在抽象類定義以前,若是其餘類中,沒有公共的基類,那麼能夠經過抽象出來共同的基類來實現共性的方法
2 若是父類中的方法有默認的實現,而且父類須要實例化,這時候能夠考慮將 父類定義成一個普通類,用虛方法來實現多態
四、例子:
class Program { static void Main(string[] args) { Animal an = new Dog(); an.Speak(); Console.ReadKey(); } } public abstract class Animal { public abstract void Speak(); } class Dog:Animal { public override void Speak() { Console.WriteLine("狗汪汪的叫"); } } class Cat:Animal { public override void Speak() { Console.WriteLine("貓喵喵的叫"); } }
五、注意事項:
1. 抽象類成員必須標記爲abstract,而且不能有任何實現,抽象成員必須下抽象類中
2.抽象類不能被實例化
3.子類繼承抽象類後,必須把父類中的全部抽象成員都重寫(除非子類也是一個抽象類,則不須要重寫)
4.抽象成員的訪問修飾符不能是private
5.在抽象類中能夠包含實例成員,而且抽象類的實例成員能夠不被子類實現
6.抽象類中是有構造函數的,雖然不能被實例化
7.若是父類的抽象方法中參數,那麼,繼承這個抽象父類的子類在重寫父類的方法時候必須傳入對應的參數。若是抽象父類的抽象方法中有返回值,那麼子類在重寫這個抽象方法的時候也必需要傳入返回值
8.若是父類中的方法有默認的實現,而且父類須要實例化,這時候能夠考慮將 父類定義成一個普通類,用虛方法來實現多態
9.若是父類中的方法沒有默認的實現。父類也不能實例化,則能夠將該類定義爲抽象類
(四) 多態的實現方式(接口)
一、接口的定義:
接口是一種編程規範、能力,c#自己並不支持類的多重繼承,可是經過接口能夠實現類的多重繼承
二、成員:
方法;索引器;事件;自動屬性(沒有字段和方法體的屬性);不能有構造函數和字段
三、使用場景:
要繼承多個基類時,基類能夠抽象爲接口
四、例子:
class Program { static void Main(string[] args) { Ichlssable ch = new Teacher(); ch.CHLSS(); Console.ReadKey(); } } interface Ichlssable { void CHLSS(); } class Driver { public void KaiChe() { Console.WriteLine("我是一名司機,能夠開車"); } } class Teacher:Driver,Ichlssable { public void Teach() { Console.WriteLine("我是一名老師,能夠教書"); base.KaiChe(); } public void CHLSS() { Console.WriteLine("我是人類,能夠吃喝拉撒睡"); } }
五、注意事項:
1.一個類繼承了接口,這個類就須要實現接口中的全部成員
2.接口不能被實例化,也就是說不能用new建立對象
3.接口的成員中不能加訪問修飾符,默認的爲public,且不能被修改,接口中的成員不能有任何實現
4.接口和接口之間能夠繼承,而且能夠多繼承
5.接口不能夠繼承一個類,而類能夠繼承一個接口(接口只能繼承類,類能夠繼承類和接口)
6.實現接口的子類必須實現該接口的所有成員
7.一個類能夠同時繼承一個類和多個接口,那麼被繼承類的名必須寫在接口名稱的前面,並用,隔開
8.顯示實現接口的目的是爲了解決方法的重命名問題,當繼承的接口中的方法和參數如出一轍時u,就要用的顯示的實現接口。,當一個抽象實現接口的時候,須要子類去實現接口。
9.接口中不能包含實現的方法