C#知識結構


C#知識結構

對於一個工作多年的程序員而言,接口、反射、索引器、事件、委托這些耳熟能詳的詞匯,提起來別說多簡單了,但是讓老司機坐在那一個人拿起一支筆,把腦海中對C#知識結構進行梳理一下,大抵是寫不了多內容的,原因是什么呢,是遺忘?當然不是,每天面對代碼的老司機當然不會遺忘。
根本的原因是知識沒有網格化。

知識結構網格化,對於現在頻繁變化的技術格局來說,是勢在必行且可以安身立命的根本
這個方法不僅使用編程菜鳥,同樣使用編程老司機。

接下來我們進入正題,先來看一副圖

知識結構

原圖,可通過本人的Github下載,連接如下

https://github.com/yuyue5945/Blog

在導圖上列出如下幾個基礎的問題,這幾個基礎問題,會一直伴隨着我們的編程生涯

什么是類?
什么是對象?
關於類與對象的關系
類和接口之間的關系
什么是封裝,繼承,多態

接下來,我們就走進C#基礎結構的世界,從新梳理一下我們的知識結構吧

基礎概念

類-繼承、封裝、多態

在 C# 語言中創建的任何項目都有類的存在,通過類能很好地體現面向對象語言中封裝、繼承、多態的特性。本節將講解 C# 中的類和定義類方式。

在前面的學習中已經多次使用過類,類定義的語法形式並不復雜,請記住 class 關鍵字,它是定義類的關鍵字。

類定義的具體語法形式如下。
類的訪問修飾符 修飾符 類名
{
類的成員
}

其中:

類的訪問修飾符:用於設定對類的訪問限制,包括 public、internal 或者不寫,用 internal 或者不寫時代表只能在當前項目中訪問類;public 則代表可以在任何項目中訪問類。

修飾符:修飾符是對類本身特點的描述,包括 abstract、sealed 和 static。abstract 是抽象的意思,使用它修飾符的類不能被實例化;sealed 修飾的類是密封類,不能 被繼承;static 修飾的類是靜態類,不能被實例化。

類名:類名用於描述類的功能,因此在定義類名時最好是具有實際意義,這樣方便用戶理解類中描述的內容。在同一個命名空間下類名必須是唯一的。

類的成員:在類中能定義的元素,主要包括字段、屬性、方法。

封裝

封裝涉及到類定義、類聲明、類成員
類成員包括字段、屬性、方法、事件。
函數成員包括構造器、析構器、屬性、方法體、方法頭等

繼承

繼承是面向對象程序設計中最重要的概念之一。繼承允許我們根據一個類來定義另一個類,這使得創建和維護應用程序變得更容易。同時也有利於重用代碼和節省開發時間。

當創建一個類時,程序員不需要完全重新編寫新的數據成員和成員函數,只需要設計一個新的類,繼承了已有的類的成員即可。這個已有的類被稱為的基類,這個新的類被稱為派生類。

繼承的思想實現了 屬於(IS-A) 關系。例如,哺乳動物 屬於(IS-A) 動物,狗 屬於(IS-A) 哺乳動物,因此狗 屬於(IS-A) 動物。

基類和派生類
一個類可以派生自多個類或接口,這意味着它可以從多個基類或接口繼承數據和函數。

類定義關鍵字

繼承講的是基類和派生類,存在單繼承、多重繼承兩種情況
基類在派生類初始化之前自動進行初始化
構造函數和析構函數不能被繼承
若類被標注了abstract則強制派生類覆蓋基類的方法,目的為了對外提供統一的方法簽名。
若類被標注了sealed則說明該類偽密封類,不可被繼承
若類被標注了partial則說明該類被分割在幾個文件中

類調用關鍵字

Base表示調用基類方法、或者被重寫的方法

New 表示重寫同名法法、隱藏父類方法

Override表示 重載父類方法

還存在組合方法的形式,不過不推薦

override--virtual

new--virtual

C# 中創建派生類的語法如下:

    <訪問修飾符符> 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();
        }
    }
}

當上面的代碼被編譯和執行時,它會產生下列結果:

總面積: 35

動態

方法重載就是一種多態

接口(Interface)

接口定義了所有類繼承接口時應遵循的語法合同。接口定義了語法合同 "是什么" 部分,派生類定義了語法合同 "怎么做" 部分。

接口定義了屬性、方法和事件,這些都是接口的成員。接口只包含了成員的聲明。成員的定義是派生類的責任。接口提供了派生類應遵循的標准結構。

接口使得實現接口的類或結構在形式上保持一致。

抽象類在某種程度上與接口類似,但是,它們大多只是用在當只有少數方法由基類聲明由派生類實現時。

定義接口: MyInterface.cs

接口使用 interface 關鍵字聲明,它與類的聲明類似。接口聲明默認是 public 的。下面是一個接口聲明的實例:

    interface IMyInterface
    {
        void MethodToImplement();
    }

以上代碼定義了接口 IMyInterface。通常接口命令以 I 字母開頭,這個接口只有一個方法 MethodToImplement(),沒有參數和返回值,當然我們可以按照需求設置參數和返回值。

值得注意的是,該方法並沒有具體的實現。

接下來我們來實現以上接口: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 類實現了 IMyInterface 接口,接口的實現與類的繼承語法格式類似:

class InterfaceImplementer : IMyInterface
繼承接口后,我們需要實現接口的方法 MethodToImplement() , 方法名必須與接口定義的方法名一致。

命名空間(Namespace)

命名空間的設計目的是提供一種讓一組名稱與其他名稱分隔開的方式。在一個命名空間中聲明的類的名稱與另一個命名空間中聲明的相同的類的名稱不沖突。

我們舉一個計算機系統中的例子,一個文件夾(目錄)中可以包含多個文件夾,每個文件夾中不能有相同的文件名,但不同文件夾中的文件可以重名。
命名空間

定義命名空間

命名空間的定義是以關鍵字 namespace 開始,后跟命名空間的名稱,如下所示:

namespace namespace_name
{
   // 代碼聲明
}
為了調用支持命名空間版本的函數或變量,會把命名空間的名稱置於前面,如下所示:

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();
   }
}

當上面的代碼被編譯和執行時,它會產生下列結果:

Inside first_space

Inside second_space

using 關鍵字

using 關鍵字表明程序使用的是給定命名空間中的名稱。例如,我們在程序中使用 System 命名空間,其中定義了類 Console。我們可以只寫:

Console.WriteLine ("Hello there");

我們可以寫完全限定名稱,如下:

System.Console.WriteLine("Hello there");

高級應用

特性(Attribute)

特性(Attribute)是用於在運行時傳遞程序中各種元素(比如類、方法、結構、枚舉、組件等)的行為信息的聲明性標簽。您可以通過使用特性向程序添加聲明性信息。一個聲明性標簽是通過放置在它所應用的元素前面的方括號([ ])來描述的。

特性(Attribute)用於添加元數據,如編譯器指令和注釋、描述、方法、類等其他信息。.Net 框架提供了兩種類型的特性:預定義特性和自定義特性。

規定特性(Attribute)

規定特性(Attribute)的語法如下:

[attribute(positional_parameters, name_parameter = value, ...)]
element

特性(Attribute)的名稱和值是在方括號內規定的,放置在它所應用的元素之前。positional_parameters 規定必需的信息,name_parameter 規定可選的信息。

預定義特性(Attribute)

.Net 框架提供了三種預定義特性:

  • AttributeUsage
  • Conditional
  • Obsolete

反射(Reflection)

反射指程序可以訪問、檢測和修改它本身狀態或行為的一種能力。

程序集包含模塊,而模塊包含類型,類型又包含成員。反射則提供了封裝程序集、模塊和類型的對象。

您可以使用反射動態地創建類型的實例,將類型綁定到現有對象,或從現有對象中獲取類型。然后,可以調用類型的方法或訪問其字段和屬性。

優缺點

優點:

1、反射提高了程序的靈活性和擴展性。

2、降低耦合性,提高自適應能力。

3、它允許程序創建和控制任何類的對象,無需提前硬編碼目標類。

缺點:

1、性能問題:使用反射基本上是一種解釋操作,用於字段和方法接入時要遠慢於直接代碼。因此反射機制主要應用在對靈活性和拓展性要求很高的系統框架上,普通程序不建議使用。

2、使用反射會模糊程序內部邏輯;程序員希望在源代碼中看到程序的邏輯,反射卻繞過了源代碼的技術,因而會帶來維護的問題,反射代碼比相應的直接代碼更復雜。

反射(Reflection)的用途

反射(Reflection)有下列用途:

  • 它允許在運行時查看特性(attribute)信息。
  • 它允許審查集合中的各種類型,以及實例化這些類型。
  • 它允許延遲綁定的方法和屬性(property)。
  • 它允許在運行時創建新類型,然后使用這些類型執行一些任務。

屬性(Property)

屬性(Property) 是類(class)、結構(structure)和接口(interface)的命名(named)成員。類或結構中的成員變量或方法稱為 域(Field)。屬性(Property)是域(Field)的擴展,且可使用相同的語法來訪問。它們使用 訪問器(accessors) 讓私有域的值可被讀寫或操作。

屬性(Property)不會確定存儲位置。相反,它們具有可讀寫或計算它們值的 訪問器(accessors)。

例如,有一個名為 Student 的類,帶有 age、name 和 code 的私有域。我們不能在類的范圍以外直接訪問這些域,但是我們可以擁有訪問這些私有域的屬性。

訪問器(Accessors)

屬性(Property)的訪問器(accessor)包含有助於獲取(讀取或計算)或設置(寫入)屬性的可執行語句。訪問器(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) 允許一個對象可以像數組一樣使用下標的方式來訪問。

當您為類定義一個索引器時,該類的行為就會像一個 虛擬數組(virtual array) 一樣。您可以使用數組訪問運算符 [ ] 來訪問該類的的成員。

語法
一維索引器的語法如下:

element-type this[int index]
{
   // get 訪問器
   get
   {
      // 返回 index 指定的值
   }

   // set 訪問器
   set
   {
      // 設置 index 指定的值
   }
}

索引器(Indexer)的用途

索引器的行為的聲明在某種程度上類似於屬性(property)。就像屬性(property),您可使用 get 和 set 訪問器來定義索引器。但是,屬性返回或設置一個特定的數據成員,而索引器返回或設置對象實例的一個特定值。換句話說,它把實例數據分為更小的部分,並索引每個部分,獲取或設置每個部分。

定義一個屬性(property)包括提供屬性名稱。索引器定義的時候不帶有名稱,但帶有 this 關鍵字,它指向對象實例。

委托(Delegate)

C# 中的委托(Delegate)類似於 C 或 C++ 中函數的指針。委托(Delegate) 是存有對某個方法的引用的一種引用類型變量。引用可在運行時被改變。

委托(Delegate)特別用於實現事件和回調方法。所有的委托(Delegate)都派生自 System.Delegate 類。

聲明委托(Delegate)

委托聲明決定了可由該委托引用的方法。委托可指向一個與其具有相同標簽的方法。

例如,假設有一個委托:

public delegate int MyDelegate (string s);

上面的委托可被用於引用任何一個帶有一個單一的 string 參數的方法,並返回一個 int 類型變量。

聲明委托的語法如下:

delegate <return type> <delegate-name> <parameter list>

實例化委托(Delegate)

一旦聲明了委托類型,委托對象必須使用 new 關鍵字來創建,且與一個特定的方法有關。當創建委托時,傳遞到 new 語句的參數就像方法調用一樣書寫,但是不帶有參數。例如:

public delegate void printString(string s);
...
printString ps1 = new printString(WriteToScreen);
printString ps2 = new printString(WriteToFile);

事件(Event)

事件(Event) 基本上說是一個用戶操作,如按鍵、點擊、鼠標移動等等,或者是一些提示信息,如系統生成的通知。應用程序需要在事件發生時響應事件。例如,中斷。

C# 中使用事件機制實現線程間的通信。

通過事件使用委托

事件在類中聲明且生成,且通過使用同一個類或其他類中的委托與事件處理程序關聯。包含事件的類用於發布事件。這被稱為 發布器(publisher) 類。其他接受該事件的類被稱為 訂閱器(subscriber) 類。事件使用 發布-訂閱(publisher-subscriber) 模型。

發布器(publisher) 是一個包含事件和委托定義的對象。事件和委托之間的聯系也定義在這個對象中。發布器(publisher)類的對象調用這個事件,並通知其他的對象。

訂閱器(subscriber) 是一個接受事件並提供事件處理程序的對象。在發布器(publisher)類中的委托調用訂閱器(subscriber)類中的方法(事件處理程序)。

聲明事件(Event)
在類的內部聲明事件,首先必須聲明該事件的委托類型。例如:

public delegate void BoilerLogHandler(string status);
然后,聲明事件本身,使用 event 關鍵字:

// 基於上面的委托定義事件
public event BoilerLogHandler BoilerEventLog;

集合(Collection)

集合(Collection)類是專門用於數據存儲和檢索的類。這些類提供了對棧(stack)、隊列(queue)、列表(list)和哈希表(hash table)的支持。大多數集合類實現了相同的接口。

集合(Collection)類服務於不同的目的,如為元素動態分配內存,基於索引訪問列表項等等。這些類創建 Object 類的對象的集合。在 C# 中,Object 類是所有數據類型的基類。

泛型(Generic)

泛型(Generic) 允許您延遲編寫類或方法中的編程元素的數據類型的規范,直到實際在程序中使用它的時候。換句話說,泛型允許您編寫一個可以與任何數據類型一起工作的類或方法。

您可以通過數據類型的替代參數編寫類或方法的規范。當編譯器遇到類的構造函數或方法的函數調用時,它會生成代碼來處理指定的數據類型。

泛型(Generic)的特性

使用泛型是一種增強程序功能的技術,具體表現在以下幾個方面:

  • 它有助於您最大限度地重用代碼、保護類型的安全以及提高性能。
  • 您可以創建泛型集合類。.NET 框架類庫在 System.Collections.Generic 命名空間中包含了一些新的泛型集合類。您可以使用這些泛型集合類來替代 System.Collections 中的集合類。
  • 您可以創建自己的泛型接口、泛型類、泛型方法、泛型事件和泛型委托。
  • 您可以對泛型類進行約束以訪問特定數據類型的方法。
  • 關於泛型數據類型中使用的類型的信息可在運行時通過使用反射獲取。

匿名方法

我們已經提到過,委托是用於引用與其具有相同標簽的方法。換句話說,您可以使用委托對象調用可由委托引用的方法。

匿名方法(Anonymous methods) 提供了一種傳遞代碼塊作為委托參數的技術。匿名方法是沒有名稱只有主體的方法。

在匿名方法中您不需要指定返回類型,它是從方法主體內的 return 語句推斷的。

編寫匿名方法的語法

匿名方法是通過使用 delegate 關鍵字創建委托實例來聲明的。例如:

delegate void NumberChanger(int n);
...
NumberChanger nc = delegate(int x)
{
    Console.WriteLine("Anonymous Method: {0}", x);
};

代碼塊 Console.WriteLine("Anonymous Method: {0}", x); 是匿名方法的主體。

委托可以通過匿名方法調用,也可以通過命名方法調用,即,通過向委托對象傳遞方法參數。

注意: 匿名方法的主體后面需要一個 ;

托管與非托管

當一個代碼塊使用 unsafe 修飾符標記時,C# 允許在函數中使用指針變量。不安全代碼或非托管代碼是指使用了指針變量的代碼塊。

指針變量

指針 是值為另一個變量的地址的變量,即,內存位置的直接地址。就像其他變量或常量,您必須在使用指針存儲其他變量地址之前聲明指針。

指針變量聲明的一般形式為:

type* var-name;

多線程

線程 被定義為程序的執行路徑。每個線程都定義了一個獨特的控制流。如果您的應用程序涉及到復雜的和耗時的操作,那么設置不同的線程執行路徑往往是有益的,每個線程執行特定的工作。

線程是輕量級進程。一個使用線程的常見實例是現代操作系統中並行編程的實現。使用線程節省了 CPU 周期的浪費,同時提高了應用程序的效率。

到目前為止我們編寫的程序是一個單線程作為應用程序的運行實例的單一的過程運行的。但是,這樣子應用程序同時只能執行一個任務。為了同時執行多個任務,它可以被划分為更小的線程。

線程生命周期

線程生命周期開始於 System.Threading.Thread 類的對象被創建時,結束於線程被終止或完成執行時。

下面列出了線程生命周期中的各種狀態:

  • 未啟動狀態:當線程實例被創建但 Start 方法未被調用時的狀況。
  • 就緒狀態:當線程准備好運行並等待 CPU 周期時的狀況。
  • 不可運行狀態:下面的幾種情況下線程是不可運行的:
    • 已經調用 Sleep 方法
    • 已經調用 Wait 方法
    • 通過 I/O 操作阻塞
  • 死亡狀態:當線程已完成執行或已中止時的狀況。

主線程

在 C# 中,System.Threading.Thread 類用於線程的工作。它允許創建並訪問多線程應用程序中的單個線程。進程中第一個被執行的線程稱為主線程。

當 C# 程序開始執行時,主線程自動創建。使用 Thread 類創建的線程被主線程的子線程調用。您可以使用 Thread 類的 CurrentThread 屬性訪問線程。

下面的程序演示了主線程的執行:

實例
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();
        }
    }
}

當上面的代碼被編譯和執行時,它會產生下列結果:

This is MainThread

博主GitHub地址

https://github.com/yuyue5945

關注公眾號不迷路

公眾號


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM