C#知識結構
對于一個工作多年的程序員而言,接口男旗、反射舶斧、索引器、事件察皇、委托這些耳熟能詳?shù)脑~匯茴厉,提起來別說多簡單了,但是讓老司機坐在那一個人拿起一支筆什荣,把腦海中對C#知識結構進行梳理一下矾缓,大抵是寫不了多內容的,原因是什么呢稻爬,是遺忘?當然不是嗜闻,每天面對代碼的老司機當然不會遺忘。
根本的原因是知識沒有網(wǎng)格化桅锄。
知識結構網(wǎng)格化琉雳,對于現(xiàn)在頻繁變化的技術格局來說,是勢在必行且可以安身立命的根本
這個方法不僅使用編程菜鳥友瘤,同樣使用編程老司機咐吼。
接下來我們進入正題,先來看一副圖
原圖商佑,可通過本人的Github下載,連接如下
https://github.com/yuyue5945/Blog
在導圖上列出如下幾個基礎的問題厢塘,這幾個基礎問題茶没,會一直伴隨著我們的編程生涯
什么是類?
什么是對象肌幽?
關于類與對象的關系
類和接口之間的關系
什么是封裝,繼承抓半,多態(tài)
接下來喂急,我們就走進C#基礎結構的世界,從新梳理一下我們的知識結構吧
- C#知識結構
- 基礎概念
- 高級應用
基礎概念
類-繼承、封裝探入、多態(tài)
在 C# 語言中創(chuàng)建的任何項目都有類的存在狡孔,通過類能很好地體現(xiàn)面向對象語言中封裝、繼承蜂嗽、多態(tài)的特性苗膝。本節(jié)將講解 C# 中的類和定義類方式。
在前面的學習中已經多次使用過類植旧,類定義的語法形式并不復雜辱揭,請記住 class 關鍵字,它是定義類的關鍵字病附。
類定義的具體語法形式如下问窃。
類的訪問修飾符 修飾符 類名
{
類的成員
}
其中:
類的訪問修飾符:用于設定對類的訪問限制,包括 public完沪、internal 或者不寫域庇,用 internal 或者不寫時代表只能在當前項目中訪問類;public 則代表可以在任何項目中訪問類丽焊。
修飾符:修飾符是對類本身特點的描述较剃,包括 abstract、sealed 和 static技健。abstract 是抽象的意思写穴,使用它修飾符的類不能被實例化;sealed 修飾的類是密封類雌贱,不能 被繼承啊送;static 修飾的類是靜態(tài)類,不能被實例化欣孤。
類名:類名用于描述類的功能馋没,因此在定義類名時最好是具有實際意義,這樣方便用戶理解類中描述的內容降传。在同一個命名空間下類名必須是唯一的篷朵。
類的成員:在類中能定義的元素,主要包括字段、屬性声旺、方法笔链。
封裝
封裝涉及到類定義、類聲明腮猖、類成員
類成員包括字段鉴扫、屬性、方法澈缺、事件坪创。
函數(shù)成員包括構造器、析構器姐赡、屬性莱预、方法體、方法頭等
繼承
繼承是面向對象程序設計中最重要的概念之一雏吭。繼承允許我們根據(jù)一個類來定義另一個類锁施,這使得創(chuàng)建和維護應用程序變得更容易。同時也有利于重用代碼和節(jié)省開發(fā)時間杖们。
當創(chuàng)建一個類時悉抵,程序員不需要完全重新編寫新的數(shù)據(jù)成員和成員函數(shù),只需要設計一個新的類摘完,繼承了已有的類的成員即可姥饰。這個已有的類被稱為的基類,這個新的類被稱為派生類孝治。
繼承的思想實現(xiàn)了 屬于(IS-A) 關系列粪。例如,哺乳動物 屬于(IS-A) 動物谈飒,狗 屬于(IS-A) 哺乳動物岂座,因此狗 屬于(IS-A) 動物。
基類和派生類
一個類可以派生自多個類或接口杭措,這意味著它可以從多個基類或接口繼承數(shù)據(jù)和函數(shù)费什。
類定義關鍵字
繼承講的是基類和派生類,存在單繼承手素、多重繼承兩種情況
基類在派生類初始化之前自動進行初始化
構造函數(shù)和析構函數(shù)不能被繼承
若類被標注了abstract則強制派生類覆蓋基類的方法鸳址,目的為了對外提供統(tǒng)一的方法簽名。
若類被標注了sealed則說明該類偽密封類泉懦,不可被繼承
若類被標注了partial則說明該類被分割在幾個文件中
類調用關鍵字
Base表示調用基類方法稿黍、或者被重寫的方法
New 表示重寫同名法法、隱藏父類方法
Override表示 重載父類方法
還存在組合方法的形式崩哩,不過不推薦
override--virtual
new--virtual
C# 中創(chuàng)建派生類的語法如下:
<訪問修飾符符> class <基類>
{
...
}
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();
}
}
}
當上面的代碼被編譯和執(zhí)行時,它會產生下列結果:
總面積: 35
動態(tài)
方法重載就是一種多態(tài)
接口(Interface)
接口定義了所有類繼承接口時應遵循的語法合同辕漂。接口定義了語法合同 "是什么" 部分呢灶,派生類定義了語法合同 "怎么做" 部分。
接口定義了屬性钉嘹、方法和事件,這些都是接口的成員鲸阻。接口只包含了成員的聲明跋涣。成員的定義是派生類的責任。接口提供了派生類應遵循的標準結構鸟悴。
接口使得實現(xiàn)接口的類或結構在形式上保持一致陈辱。
抽象類在某種程度上與接口類似换淆,但是掀淘,它們大多只是用在當只有少數(shù)方法由基類聲明由派生類實現(xiàn)時。
定義接口: MyInterface.cs
接口使用 interface 關鍵字聲明废登,它與類的聲明類似震贵。接口聲明默認是 public 的利赋。下面是一個接口聲明的實例:
interface IMyInterface
{
void MethodToImplement();
}
以上代碼定義了接口 IMyInterface。通常接口命令以 I 字母開頭猩系,這個接口只有一個方法 MethodToImplement()媚送,沒有參數(shù)和返回值,當然我們可以按照需求設置參數(shù)和返回值寇甸。
值得注意的是塘偎,該方法并沒有具體的實現(xiàn)。
接下來我們來實現(xiàn)以上接口:InterfaceImplementer.cs
實例
using System;
interface IMyInterface
{
// 接口成員
void MethodToImplement();
}
class InterfaceImplementer : IMyInterface
{
static void Main()
{
InterfaceImplementer iImp = new InterfaceImplementer();
iImp.MethodToImplement();
}
public void MethodToImplement()
{
Console.WriteLine("MethodToImplement() called.");
}
}
InterfaceImplementer 類實現(xiàn)了 IMyInterface 接口拿霉,接口的實現(xiàn)與類的繼承語法格式類似:
class InterfaceImplementer : IMyInterface
繼承接口后吟秩,我們需要實現(xiàn)接口的方法 MethodToImplement() , 方法名必須與接口定義的方法名一致。
命名空間(Namespace)
命名空間的設計目的是提供一種讓一組名稱與其他名稱分隔開的方式绽淘。在一個命名空間中聲明的類的名稱與另一個命名空間中聲明的相同的類的名稱不沖突涵防。
我們舉一個計算機系統(tǒng)中的例子,一個文件夾(目錄)中可以包含多個文件夾收恢,每個文件夾中不能有相同的文件名武学,但不同文件夾中的文件可以重名。
定義命名空間
命名空間的定義是以關鍵字 namespace 開始伦意,后跟命名空間的名稱火窒,如下所示:
namespace namespace_name
{
// 代碼聲明
}
為了調用支持命名空間版本的函數(shù)或變量,會把命名空間的名稱置于前面驮肉,如下所示:
namespace_name.item_name;
下面的程序演示了命名空間的用法:
實例
using System;
namespace first_space
{
class namespace_cl
{
public void func()
{
Console.WriteLine("Inside first_space");
}
}
}
namespace second_space
{
class namespace_cl
{
public void func()
{
Console.WriteLine("Inside second_space");
}
}
}
class TestClass
{
static void Main(string[] args)
{
first_space.namespace_cl fc = new first_space.namespace_cl();
second_space.namespace_cl sc = new second_space.namespace_cl();
fc.func();
sc.func();
Console.ReadKey();
}
}
當上面的代碼被編譯和執(zhí)行時熏矿,它會產生下列結果:
Inside first_space
Inside second_space
using 關鍵字
using 關鍵字表明程序使用的是給定命名空間中的名稱。例如,我們在程序中使用 System 命名空間票编,其中定義了類 Console褪储。我們可以只寫:
Console.WriteLine ("Hello there");
我們可以寫完全限定名稱,如下:
System.Console.WriteLine("Hello there");
高級應用
特性(Attribute)
特性(Attribute)是用于在運行時傳遞程序中各種元素(比如類慧域、方法鲤竹、結構、枚舉昔榴、組件等)的行為信息的聲明性標簽辛藻。您可以通過使用特性向程序添加聲明性信息。一個聲明性標簽是通過放置在它所應用的元素前面的方括號([ ])來描述的互订。
特性(Attribute)用于添加元數(shù)據(jù)吱肌,如編譯器指令和注釋、描述仰禽、方法氮墨、類等其他信息。.Net 框架提供了兩種類型的特性:預定義特性和自定義特性吐葵。
規(guī)定特性(Attribute)
規(guī)定特性(Attribute)的語法如下:
[attribute(positional_parameters, name_parameter = value, ...)]
element
特性(Attribute)的名稱和值是在方括號內規(guī)定的规揪,放置在它所應用的元素之前。positional_parameters 規(guī)定必需的信息折联,name_parameter 規(guī)定可選的信息粒褒。
預定義特性(Attribute)
.Net 框架提供了三種預定義特性:
- AttributeUsage
- Conditional
- Obsolete
反射(Reflection)
反射指程序可以訪問、檢測和修改它本身狀態(tài)或行為的一種能力诚镰。
程序集包含模塊奕坟,而模塊包含類型,類型又包含成員清笨。反射則提供了封裝程序集月杉、模塊和類型的對象。
您可以使用反射動態(tài)地創(chuàng)建類型的實例抠艾,將類型綁定到現(xiàn)有對象苛萎,或從現(xiàn)有對象中獲取類型。然后检号,可以調用類型的方法或訪問其字段和屬性腌歉。
優(yōu)缺點
優(yōu)點:
1、反射提高了程序的靈活性和擴展性齐苛。
2翘盖、降低耦合性,提高自適應能力凹蜂。
3馍驯、它允許程序創(chuàng)建和控制任何類的對象阁危,無需提前硬編碼目標類。
缺點:
1汰瘫、性能問題:使用反射基本上是一種解釋操作狂打,用于字段和方法接入時要遠慢于直接代碼。因此反射機制主要應用在對靈活性和拓展性要求很高的系統(tǒng)框架上混弥,普通程序不建議使用趴乡。
2、使用反射會模糊程序內部邏輯剑逃;程序員希望在源代碼中看到程序的邏輯浙宜,反射卻繞過了源代碼的技術,因而會帶來維護的問題蛹磺,反射代碼比相應的直接代碼更復雜。
反射(Reflection)的用途
反射(Reflection)有下列用途:
- 它允許在運行時查看特性(attribute)信息同仆。
- 它允許審查集合中的各種類型萤捆,以及實例化這些類型。
- 它允許延遲綁定的方法和屬性(property)俗批。
- 它允許在運行時創(chuàng)建新類型俗或,然后使用這些類型執(zhí)行一些任務。
屬性(Property)
屬性(Property) 是類(class)岁忘、結構(structure)和接口(interface)的命名(named)成員辛慰。類或結構中的成員變量或方法稱為 域(Field)。屬性(Property)是域(Field)的擴展干像,且可使用相同的語法來訪問帅腌。它們使用 訪問器(accessors) 讓私有域的值可被讀寫或操作。
屬性(Property)不會確定存儲位置麻汰。相反速客,它們具有可讀寫或計算它們值的 訪問器(accessors)。
例如五鲫,有一個名為 Student 的類溺职,帶有 age、name 和 code 的私有域位喂。我們不能在類的范圍以外直接訪問這些域浪耘,但是我們可以擁有訪問這些私有域的屬性。
訪問器(Accessors)
屬性(Property)的訪問器(accessor)包含有助于獲人苎隆(讀取或計算)或設置(寫入)屬性的可執(zhí)行語句七冲。訪問器(accessor)聲明可包含一個 get 訪問器、一個 set 訪問器弃舒,或者同時包含二者癞埠。例如:
// 聲明類型為 string 的 Code 屬性
public string Code
{
get
{
return code;
}
set
{
code = value;
}
}
// 聲明類型為 string 的 Name 屬性
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
// 聲明類型為 int 的 Age 屬性
public int Age
{
get
{
return age;
}
set
{
age = value;
}
}
索引器(Indexer)
索引器(Indexer) 允許一個對象可以像數(shù)組一樣使用下標的方式來訪問状原。
當您為類定義一個索引器時,該類的行為就會像一個 虛擬數(shù)組(virtual array) 一樣苗踪。您可以使用數(shù)組訪問運算符 [ ] 來訪問該類的的成員颠区。
語法
一維索引器的語法如下:
element-type this[int index]
{
// get 訪問器
get
{
// 返回 index 指定的值
}
// set 訪問器
set
{
// 設置 index 指定的值
}
}
索引器(Indexer)的用途
索引器的行為的聲明在某種程度上類似于屬性(property)。就像屬性(property)通铲,您可使用 get 和 set 訪問器來定義索引器毕莱。但是,屬性返回或設置一個特定的數(shù)據(jù)成員颅夺,而索引器返回或設置對象實例的一個特定值朋截。換句話說,它把實例數(shù)據(jù)分為更小的部分吧黄,并索引每個部分部服,獲取或設置每個部分。
定義一個屬性(property)包括提供屬性名稱拗慨。索引器定義的時候不帶有名稱廓八,但帶有 this 關鍵字,它指向對象實例赵抢。
委托(Delegate)
C# 中的委托(Delegate)類似于 C 或 C++ 中函數(shù)的指針剧蹂。委托(Delegate) 是存有對某個方法的引用的一種引用類型變量。引用可在運行時被改變烦却。
委托(Delegate)特別用于實現(xiàn)事件和回調方法宠叼。所有的委托(Delegate)都派生自 System.Delegate 類。
聲明委托(Delegate)
委托聲明決定了可由該委托引用的方法其爵。委托可指向一個與其具有相同標簽的方法冒冬。
例如,假設有一個委托:
public delegate int MyDelegate (string s);
上面的委托可被用于引用任何一個帶有一個單一的 string 參數(shù)的方法醋闭,并返回一個 int 類型變量窄驹。
聲明委托的語法如下:
delegate <return type> <delegate-name> <parameter list>
實例化委托(Delegate)
一旦聲明了委托類型,委托對象必須使用 new 關鍵字來創(chuàng)建证逻,且與一個特定的方法有關乐埠。當創(chuàng)建委托時,傳遞到 new 語句的參數(shù)就像方法調用一樣書寫囚企,但是不帶有參數(shù)丈咐。例如:
public delegate void printString(string s);
...
printString ps1 = new printString(WriteToScreen);
printString ps2 = new printString(WriteToFile);
事件(Event)
事件(Event) 基本上說是一個用戶操作,如按鍵龙宏、點擊棵逊、鼠標移動等等,或者是一些提示信息银酗,如系統(tǒng)生成的通知辆影。應用程序需要在事件發(fā)生時響應事件徒像。例如,中斷蛙讥。
C# 中使用事件機制實現(xiàn)線程間的通信锯蛀。
通過事件使用委托
事件在類中聲明且生成,且通過使用同一個類或其他類中的委托與事件處理程序關聯(lián)次慢。包含事件的類用于發(fā)布事件旁涤。這被稱為 發(fā)布器(publisher) 類。其他接受該事件的類被稱為 訂閱器(subscriber) 類迫像。事件使用 發(fā)布-訂閱(publisher-subscriber) 模型劈愚。
發(fā)布器(publisher) 是一個包含事件和委托定義的對象。事件和委托之間的聯(lián)系也定義在這個對象中闻妓。發(fā)布器(publisher)類的對象調用這個事件菌羽,并通知其他的對象。
訂閱器(subscriber) 是一個接受事件并提供事件處理程序的對象由缆。在發(fā)布器(publisher)類中的委托調用訂閱器(subscriber)類中的方法(事件處理程序)算凿。
聲明事件(Event)
在類的內部聲明事件,首先必須聲明該事件的委托類型犁功。例如:
public delegate void BoilerLogHandler(string status);
然后,聲明事件本身婚夫,使用 event 關鍵字:
// 基于上面的委托定義事件
public event BoilerLogHandler BoilerEventLog;
集合(Collection)
集合(Collection)類是專門用于數(shù)據(jù)存儲和檢索的類浸卦。這些類提供了對棧(stack)、隊列(queue)案糙、列表(list)和哈希表(hash table)的支持限嫌。大多數(shù)集合類實現(xiàn)了相同的接口。
集合(Collection)類服務于不同的目的时捌,如為元素動態(tài)分配內存怒医,基于索引訪問列表項等等。這些類創(chuàng)建 Object 類的對象的集合奢讨。在 C# 中稚叹,Object 類是所有數(shù)據(jù)類型的基類。
泛型(Generic)
泛型(Generic) 允許您延遲編寫類或方法中的編程元素的數(shù)據(jù)類型的規(guī)范拿诸,直到實際在程序中使用它的時候扒袖。換句話說,泛型允許您編寫一個可以與任何數(shù)據(jù)類型一起工作的類或方法亩码。
您可以通過數(shù)據(jù)類型的替代參數(shù)編寫類或方法的規(guī)范季率。當編譯器遇到類的構造函數(shù)或方法的函數(shù)調用時,它會生成代碼來處理指定的數(shù)據(jù)類型描沟。
泛型(Generic)的特性
使用泛型是一種增強程序功能的技術飒泻,具體表現(xiàn)在以下幾個方面:
- 它有助于您最大限度地重用代碼鞭光、保護類型的安全以及提高性能。
- 您可以創(chuàng)建泛型集合類泞遗。.NET 框架類庫在 System.Collections.Generic 命名空間中包含了一些新的泛型集合類惰许。您可以使用這些泛型集合類來替代 System.Collections 中的集合類。
- 您可以創(chuàng)建自己的泛型接口刹孔、泛型類啡省、泛型方法、泛型事件和泛型委托髓霞。
- 您可以對泛型類進行約束以訪問特定數(shù)據(jù)類型的方法卦睹。
- 關于泛型數(shù)據(jù)類型中使用的類型的信息可在運行時通過使用反射獲取。
匿名方法
我們已經提到過方库,委托是用于引用與其具有相同標簽的方法结序。換句話說,您可以使用委托對象調用可由委托引用的方法纵潦。
匿名方法(Anonymous methods) 提供了一種傳遞代碼塊作為委托參數(shù)的技術徐鹤。匿名方法是沒有名稱只有主體的方法。
在匿名方法中您不需要指定返回類型邀层,它是從方法主體內的 return 語句推斷的返敬。
編寫匿名方法的語法
匿名方法是通過使用 delegate 關鍵字創(chuàng)建委托實例來聲明的。例如:
delegate void NumberChanger(int n);
...
NumberChanger nc = delegate(int x)
{
Console.WriteLine("Anonymous Method: {0}", x);
};
代碼塊 Console.WriteLine("Anonymous Method: {0}", x); 是匿名方法的主體寥院。
委托可以通過匿名方法調用劲赠,也可以通過命名方法調用,即秸谢,通過向委托對象傳遞方法參數(shù)凛澎。
注意: 匿名方法的主體后面需要一個 ;
托管與非托管
當一個代碼塊使用 unsafe 修飾符標記時,C# 允許在函數(shù)中使用指針變量估蹄。不安全代碼或非托管代碼是指使用了指針變量的代碼塊塑煎。
指針變量
指針 是值為另一個變量的地址的變量,即臭蚁,內存位置的直接地址最铁。就像其他變量或常量,您必須在使用指針存儲其他變量地址之前聲明指針刊棕。
指針變量聲明的一般形式為:
type* var-name;
多線程
線程 被定義為程序的執(zhí)行路徑炭晒。每個線程都定義了一個獨特的控制流。如果您的應用程序涉及到復雜的和耗時的操作甥角,那么設置不同的線程執(zhí)行路徑往往是有益的网严,每個線程執(zhí)行特定的工作。
線程是輕量級進程嗤无。一個使用線程的常見實例是現(xiàn)代操作系統(tǒng)中并行編程的實現(xiàn)震束。使用線程節(jié)省了 CPU 周期的浪費怜庸,同時提高了應用程序的效率。
到目前為止我們編寫的程序是一個單線程作為應用程序的運行實例的單一的過程運行的垢村。但是割疾,這樣子應用程序同時只能執(zhí)行一個任務。為了同時執(zhí)行多個任務嘉栓,它可以被劃分為更小的線程宏榕。
線程生命周期
線程生命周期開始于 System.Threading.Thread 類的對象被創(chuàng)建時,結束于線程被終止或完成執(zhí)行時侵佃。
下面列出了線程生命周期中的各種狀態(tài):
- 未啟動狀態(tài):當線程實例被創(chuàng)建但 Start 方法未被調用時的狀況麻昼。
- 就緒狀態(tài):當線程準備好運行并等待 CPU 周期時的狀況。
- 不可運行狀態(tài):下面的幾種情況下線程是不可運行的:
- 已經調用 Sleep 方法
- 已經調用 Wait 方法
- 通過 I/O 操作阻塞
- 死亡狀態(tài):當線程已完成執(zhí)行或已中止時的狀況馋辈。
主線程
在 C# 中抚芦,System.Threading.Thread 類用于線程的工作。它允許創(chuàng)建并訪問多線程應用程序中的單個線程迈螟。進程中第一個被執(zhí)行的線程稱為主線程叉抡。
當 C# 程序開始執(zhí)行時,主線程自動創(chuàng)建答毫。使用 Thread 類創(chuàng)建的線程被主線程的子線程調用褥民。您可以使用 Thread 類的 CurrentThread 屬性訪問線程。
下面的程序演示了主線程的執(zhí)行:
實例
using System;
using System.Threading;
namespace MultithreadingApplication
{
class MainThreadProgram
{
static void Main(string[] args)
{
Thread th = Thread.CurrentThread;
th.Name = "MainThread";
Console.WriteLine("This is {0}", th.Name);
Console.ReadKey();
}
}
}
當上面的代碼被編譯和執(zhí)行時洗搂,它會產生下列結果:
This is MainThread