天天看點

C#_08_官方文檔_語言介紹C# 語言介紹程式結構類型和變量表達式語句類和對象結構數組接口枚舉委托特性

C# 官方文檔_C#語言介紹篇章

https://docs.microsoft.com/zh-cn/dotnet/csharp/tour-of-csharp/

C# 語言介紹

C#(讀作“See Sharp”)是一種簡單易用的新式程式設計語言,不僅面向對象,還類型安全。 

C# 源于 C 語言系列,C、C++、Java 和 JavaScript 程式員很快就可以上手使用。

C# 是一種面向對象的語言。不僅如此,C# 還進一步支援面向元件的程式設計。 

當代軟體設計越來越依賴采用自描述的獨立功能包形式的軟體元件。

 此類元件的關鍵特征包括:

1. 為程式設計模型提供屬性、方法和事件;

2. 包含提供元件聲明性資訊的特性;

3. 包含自己的文檔。 

C# 提供了語言構造來直接支援這些概念,讓 C# 成為一種非常自然的語言,可用于建立和使用軟體元件。

多項 C# 功能有助于構造可靠耐用的應用程式:

1. 垃圾回收可自動回收無法通路的未使用對象占用的記憶體;

2. 異常處理提供了一種結構化的可擴充方法來執行錯誤檢測和恢複;

3. C# 語言的類型安全設計禁止讀取未初始化的變量、為範圍之外的數組編制索引或執行未檢查的類型轉換。

C# 采用統一的類型系統。 所有 C# 類型(包括 

int

 和 

double

 等基元類型)均繼承自一個根 

object

 類型。 

是以,所有類型共用一組通用運算,任何類型的值都可以一緻地進行存儲、傳輸和處理。 

此外,C# 還支援使用者定義的引用類型和值類型,進而支援對象動态配置設定以及輕量級結構的内嵌式存儲。

為了確定 C# 程式和庫能夠随着時間的推移以相容的方式發展,C# 設計更強調版本控制。 

許多程式設計語言很少關注這個問題,是以,當引入新版依賴庫時,用這些語言編寫的程式會出現更多不必要的中斷現象。 

由于更強調版本控制,直接受影響的 C# 設計方面包括:

單獨的 

virtual

 和 

override

 修飾符、

關于方法重載決策的規則,

以及對顯式接口成員聲明的支援。

Hello world

“Hello, World”程式曆來都用于介紹程式設計語言。 

下面展示了此程式的 C# 代碼:

using System;
class Hello
{
    static void Main()
    {
        Console.WriteLine("Hello, Beyond");
    }
}
           

C# 源檔案的檔案擴充名通常為 

.cs

。 

假設“Hello, World”程式存儲在檔案 

hello.cs

 中,則可以使用下列指令行編譯此程式:

csc hello.cs
           

這會生成 hello.exe 可執行程式集。 

運作此應用程式生成以下輸出:

Hello, Beyond
           

重要注意事項: 

編譯 

csc

 指令實作的是完整架構,可能并不所有平台都适用。

“Hello, World”程式始于引用 

System

 命名空間的 

using

 指令。

 命名空間提供了一種用于組織 C# 程式和庫的分層方法。 

命名空間包含類型和其他命名空間。

例如,

System

 命名空間包含許多類型(如程式中引用的 

Console

 類)和其他許多命名空間(如 

IO

 和 

Collections

)。 

借助引用給定命名空間的 

using

指令,可以非限定的方式使用作為相應命名空間成員的類型。 

由于使用 

using

 指令,是以程式可以使用 

Console.WriteLine

 作為 

System.Console.WriteLine

 的簡寫。

“Hello, World”程式聲明的 

Hello

 類隻有一個成員,即 

Main

 方法。 

Main

 方法是使用靜态修飾符進行聲明。 

執行個體方法可以使用關鍵字 

this

 引用特定的封閉對象執行個體,

而靜态方法則可以在不引用特定對象的情況下運作。 

按照約定,

Main

 靜态方法是程式的入口點。

程式的輸出是由 

System

 命名空間中 

Console

 類的 

WriteLine

 方法生成。 

此類由标準類庫提供。預設情況下,編譯器會自動引用标準類庫。

關于 C#,要介紹的内容還有很多。 

下面各主題概述了 C# 語言元素。 

通過這些概述,可以了解 C# 語言所有元素的基本資訊,并獲得深入了解 C# 語言元素所需的資訊:

  • 程式結構
    • 了解 C# 語言中的關鍵組織概念:程式、命名空間、類型、成員和程式集。
  • 類型和變量
    • 了解 C# 語言中的值類型、引用類型和變量。
  • 表達式
    • 表達式是在操作數和運算符的基礎之上構造而成。 表達式生成的是值。
  • 語句
    • 語句用于表示程式的操作。
  • 類和對象
    • 類是最基本的 C# 類型。 對象是類執行個體。 類是使用成員生成的,此主題也對此進行了介紹。
  • 結構
    • 與類不同,結構是屬于值類型的資料結構。
  • 數組
    • 數組是一種資料結構,其中包含許多通過計算索引通路的變量。
  • 接口
    • 接口定義了可由類和結構實作的協定。 接口可以包含方法、屬性、事件和索引器。 接口不提供所定義的成員的實作代碼,僅指定必須由實作接口的類或結構提供的成員。
  • 枚舉
    • 枚舉類型是包含一組已命名常量的獨特值類型。
  • 委托
    • 委托類型表示對具有特定參數清單和傳回類型的方法的引用。 通過委托,可以将方法視為可配置設定給變量并可作為參數傳遞的實體。 委托類似于其他一些語言中的函數指針概念,但與函數指針不同的是,委托不僅面向對象,還類型安全。
  • 特性
    • 使用特性,程式可以指定關于類型、成員和其他實體的附加聲明性資訊。

程式結構

C# 中的關鍵組織結構概念包括程式、命名空間、類型、成員和程式集。 

C# 程式由一個或多個源檔案組成。 

程式聲明類型,而類型則包含成員,并被整理到命名空間中。 

類型示例包括類和接口。 

成員示例包括字段、方法、屬性和事件。 

編譯完的 C# 程式實際上會打包到程式集中。 

程式集的檔案擴充名通常為 

.exe

 或 

.dll

,具體取決于實作的是應用程式還是庫。

以下示例在 

Acme.Collections

 命名空間中聲明 

Stack

 類:

using System;
namespace Acme.Collections
{
    public class Stack
    {
        Entry top;
        public void Push(object data) 
        {
            top = new Entry(top, data);
        }

        public object Pop() 
        {
            if (top == null)
            {
                throw new InvalidOperationException();
            }
            object result = top.data;
            top = top.next;
            return result;
        }
        
        class Entry
        {
            public Entry next;
            public object data;
            public Entry(Entry next, object data)
            {
                this.next = next;
                this.data = data;
            }
        }
    }
}
           

此類的完全限定的名稱為 

Acme.Collections.Stack

。 

此類包含多個成員:一個 

top

 字段、兩個方法(

Push

 和 

Pop

)和一個 

Entry

 嵌套類。 

Entry

 類還包含三個成員:一個 

next

 字段、一個 

data

 字段和一個構造函數。 

假定示例的源代碼存儲在 

acme.cs

 檔案中,以下指令行

csc /t:library acme.cs
           

将示例編譯成庫(不含 

Main

 入口點的代碼),并生成 

acme.dll

 程式集。

重要注意事項: 

上述示例使用 

csc

 作為指令行 C# 編譯器。 

此編譯器是 Windows 可執行檔案。 

若要在其他平台上使用 C#,應使用 .NET Core 工具。 .

NET Core 生态系統使用 

dotnet

 CLI 來管理指令行生成。 

這包括管理依賴項和調用 C# 編譯器。 

有關在 .NET Core 支援的平台上使用這些工具的完整說明,請參閱這篇教程。

程式集包含中間語言 (IL) 指令形式的可執行代碼和中繼資料形式的符号資訊。 

執行前,程式集中的 IL 代碼會被 .NET 公共語言運作時的實時 (JIT) 編譯器自動轉換成處理器專屬代碼。

由于程式集是包含代碼和中繼資料的自描述功能單元,是以無需在 C# 中使用 

#include

 指令和頭檔案。 

隻需在編譯程式時引用特定的程式集,即可在 C# 程式中使用此程式集中包含的公共類型和成員。 

例如,此程式使用 

acme.dll

 程式集中的 

Acme.Collections.Stack

 類:

using System;
using Acme.Collections;
class Example
{
    static void Main() 
    {
        Stack s = new Stack();
        s.Push(1);
        s.Push(10);
        s.Push(100);
        Console.WriteLine(s.Pop());
        Console.WriteLine(s.Pop());
        Console.WriteLine(s.Pop());
    }
}
           

如果程式存儲在檔案 

example.cs

 中,

那麼在 

example.cs

 編譯完後,可以使用編譯器的 /r 選項引用 acme.dll 程式集:

csc /r:acme.dll example.cs
           

這會建立 

example.exe

 可執行程式集,它将在運作時輸出以下内容:

100
10
1
           

使用 C#,可以将程式的源文本存儲在多個源檔案中。 

編譯多檔案 C# 程式時,可以将所有源檔案一起處理,并且源檔案可以随意互相引用。

從概念上講,就像是所有源檔案在處理前被集中到一個大檔案中一樣。

在 C# 中,永遠都不需要使用前向聲明,因為聲明順序無關緊要(除了極少數的例外情況)。 

C# 并不限制源檔案隻能聲明一種公共類型,也不要求源檔案的檔案名必須與其中聲明的類型相比對。

類型和變量

C# 有兩種類型:值類型和引用類型。 

值類型的變量直接包含資料,而引用類型的變量則存儲對資料(稱為“對象”)的引用。 

對于引用類型,兩個變量可以引用同一對象;

是以,對一個變量執行的運算可能會影響另一個變量引用的對象。 

借助值類型,每個變量都有自己的資料副本;

是以,對一個變量執行的運算不會影響另一個變量(

ref

 和 

out

 參數變量除外)。

C# 值類型又細分為簡單類型、枚舉類型、結構類型和可以為 null 的值類型。 

C# 引用類型又細分為類類型、接口類型、數組類型和委托類型。

下面概述了 C# 的類型系統。

  • 值類型
    • 簡單類型
      • 有符号的整型:

        sbyte

        short

        int

        long

      • 無符号的整型:

        byte

        ushort

        uint

        ulong

      • Unicode 字元:

        char

      • IEEE 浮點:

        float

        double

      • 高精度小數:

        decimal

      • 布爾:

        bool

    • 枚舉類型
      • 格式為 

        enum E {...}

         的使用者定義類型
    • 結構類型
      • 格式為 

        struct S {...}

         的使用者定義類型
    • 可以為 null 的值類型
      • 值為 

        null

         的其他所有值類型的擴充
  • 引用類型
    • 類類型
      • 其他所有類型的最終基類:

        object

      • Unicode 字元串:

        string

      • 格式為 

        class C {...}

         的使用者定義類型
    • 接口類型
      • 格式為 

        interface I {...}

         的使用者定義類型
    • 數組類型
      • 一維和多元,例如 

        int[]

         和 

        int[,]

    • 委托類型
      • 格式為 

        delegate int D(...)

         的使用者定義類型

八個整型類型支援帶符号或不帶符号格式的 8 位、16 位、32 位和 64 位值。

兩個浮點類型(

float

 和 

double

)分别使用 32 位單精度和 64 位雙精度 IEC-60559 格式表示。

decimal

 類型是适用于财務和貨币計算的 128 位資料類型。

C# 的 

bool

 類型用于表示布爾值(

true

 或 

false

)。

C# 使用 Unicode 編碼處理字元和字元串。 

char

 類型表示 UTF-16 代碼單元,

string

 類型表示一系列 UTF-16 代碼單元。

下面總結了 C# 的數值類型。

  • 有符号的整型
    • sbyte

      :8 位,介于 -128 到 127 之間
    • short

      :16 位,介于 -32,768 到 32,767 之間
    • int

      :32 位,介于 -2,147,483,648 到 2,147,483,647 之間
    • long

      :64 位,介于 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 之間
  • 無符号的整型
    • byte

      :8 位,介于 0 到 255 之間
    • ushort

      :16 位,介于 0 到 65,535 之間
    • uint

      :32 位,介于 0 到 4,294,967,295 之間
    • ulong

      :64 位,介于 0 到 18,446,744,073,709,551,615 之間
  • 浮點
    • float

      :32 位,介于 1.5 × 10-45 到 3.4 × 1038 之間,7 位精度
    • double

      :64 位,介于 5.0 × 10-324 到 1.7 × 10308 之間,15 位精度
  • 十進制
    • decimal

      :128 位,至少介于 -7.9 × 10-28 到 7.9 × 1028 之間,至少為 28 位精度

C# 程式使用類型聲明建立新類型。 

類型聲明指定新類型的名稱和成員。 

使用者可定義以下五種 C# 類型:類類型、結構類型、接口類型、枚舉類型和委托類型。

class

 類型定義包含資料成員(字段)和函數成員(方法、屬性及其他)的資料結構。 

類類型支援單一繼承和多形性,即派生類可以擴充和專門針對基類的機制。

struct

 類型定義包含資料成員和函數成員的結構,這一點與類類型相似。 

不過,與類不同的是,結構是值類型,通常不需要進行堆配置設定。 

結構類型不支援使用者指定的繼承,并且所有結構類型均隐式繼承自類型 

object

interface

 類型将協定定義為一組已命名的公共函數成員。 

實作 

interface

 的 

class

 或 

struct

 必須提供接口函數成員的實作代碼。 

interface

 可以繼承自多個基接口,

class

 和 

struct

 可以實作多個接口。

delegate

 類型表示引用包含特定參數清單和傳回類型的方法。 

通過委托,可以将方法視為可配置設定給變量并可作為參數傳遞的實體。 

委托類同于函數式語言提供的函數類型。 

委托也類似于其他一些語言中的函數指針概念,

但與函數指針不同的是,委托不僅面向對象,還類型安全。

class

struct

interface

 和 

delegate

 類型全部都支援泛型,是以可以使用其他類型對它們進行參數化。

enum

 類型是一種包含已命名常量的獨特類型。 

每個 

enum

 類型都有一個基礎類型(必須是八種整型類型之一)。 

enum

 類型的值集與基礎類型的值集相同。

C# 支援任意類型的一維和多元數組。 與上述類型不同,數組類型無需先聲明即可使用。 

相反,數組類型是通過在類型名稱後面添加方括号構造而成。 

例如,

int[]

 是 

int

 類型的一維數組,

int[,]

 是 

int

類型的二維數組,

int[][]

 是由 

int

 類型的一維數組構成的一維數組。

可以為 null 的值類型也無需先聲明即可使用。 

對于所有不可以為 null 的值類型 

T

,都有對應的可以為 null 的值類型 

T?

,後者可以包含附加值 

null

。 

例如,

int?

 是可以包含任何 32 位整數或值 

null

 的類型。

C# 采用統一的類型系統,是以任意類型的值都可視為 

object

。 

每種 C# 類型都直接或間接地派生自 

object

 類類型,而 

object

 是所有類型的最終基類。 

隻需将值視為類型 

object

,即可将引用類型的值視為對象。 

通過執行裝箱和取消裝箱操作,可以将值類型的值視為對象。 

在以下示例中,

int

 值被轉換成 

object

,然後又恢複成 

int

using System;
class BoxingExample
{
    static void Main()
    {
        int i = 123;
        object o = i;    // Boxing
        int j = (int)o;  // Unboxing
    }
}
           

當值類型的值轉換成 

object

 類型時,将配置設定 

object

 執行個體(亦稱為“箱”)來包含值,然後該值會複制到相應的箱中。 

相反,當 

object

 引用被顯式轉換成值類型時,将檢查引用的 

object

 是否是具有正确值類型的箱;

如果檢查成功,則會将箱中的值複制出來。

C# 的統一類型系統實際上意味着可以“按需”将值類型轉換成對象。 

鑒于這種統一性,使用類型 

object

 的正常用途庫可以與引用類型和值類型結合使用。

C# 有多種變量,其中包括字段、數組元素、局部變量和參數。 

變量表示存儲位置,每個變量都具有一種類型,用于确定可以在變量中存儲哪些值,如下文所述。

  • 不可以為 null 的值類型
    • 具有精确類型的值
  • 可以為 null 的值類型
    • null

       值或具有精确類型的值
  • object
    • null

       引用、對任意引用類型的對象的引用,或對任意值類型的裝箱值的引用
  • 類類型
    • null

       引用、對類類型執行個體的引用,或對派生自類類型的類執行個體的引用
  • 接口類型
    • null

       引用、對實作接口類型的類類型執行個體的引用,或對實作接口類型的值類型的裝箱值的引用
  • 數組類型
    • null

       引用、對數組類型執行個體的引用,或對相容的數組類型執行個體的引用
  • 委托類型
    • null

       引用或對相容的委托類型執行個體的引用

表達式

表達式是在操作數和運算符的基礎之上構造而成。 

表達式的運算符指明了向操作數應用的運算。 

運算符的示例包括 

+

-

*

/

 和 

new

。 

操作數的示例包括文本、字段、局部變量和表達式。

如果表達式包含多個運算符,那麼運算符的優先級決定了各個運算符的計算順序。 

例如,表達式 

x + y * z

 相當于計算 

x + (y * z)

,因為 

*

 運算符的優先級高于 

+

 運算符。

如果操作數兩邊的兩個運算符的優先級相同,那麼運算符的結合性決定了運算的執行順序:

  • 除了指派運算符之外,所有二進制運算符均為左結合運算符,即從左向右執行運算。 例如,

    x + y + z

    将計算為 

    (x + y) + z

  • 指派運算符和條件運算符 (

    ?:

    ) 為右結合運算符,即從右向左執行運算。 例如,

    x = y = z

     将計算為 

    x = (y = z)

可以使用括号控制優先級和結合性。 

例如,

x + y * z

 先計算 

y

 乘 

z

,并将結果與 

x

 相加,

而 

(x + y) * z

 則先計算 

x

 加 

y

,然後将結果與 

z

 相乘。

大多數運算符都可以重載。 

借助運算符重載,可以為一個或兩個操作數為使用者定義類或結構類型的運算指定使用者定義運算符實作代碼。

下面總結了 C# 運算符,按優先級從高到低的順序列出了各類運算符。 

同一類别的運算符的優先級也相同。 

每個類别下均列出了相應類别的表達式,以及對每種表達式類型的說明。

  • 基本
    • x.m

      :成員通路
    • x(...)

      :方法和委托調用
    • x[...]

      :數組和索引器通路
    • x++

      :後置遞增
    • x--

      :後置遞減
    • new T(...)

      :建立對象和委托
    • new T(...){...}

      :使用初始值設定項的對象建立
    • new {...}

      :匿名對象初始值設定項
    • new T[...]

      :數組建立
    • typeof(T)

      :擷取 

      T

       的 Type 對象
    • checked(x)

      :在已檢查的上下文中計算表達式
    • unchecked(x)

      :在未檢查的上下文中計算表達式
    • default(T)

      :擷取類型為 

      T

       的預設值
    • delegate {...}

      :匿名函數(匿名方法)
  • 一進制
    • +x

      :辨別
    • -x

      :取反
    • !x

      :邏輯取反
    • ~x

      :按位取反
    • ++x

      :前置遞增
    • --x

      :前置遞減
    • (T)x

      :将 

      x

       顯式轉換成類型 

      T

    • await x

      :異步等待 

      x

       完成
  • 乘法
    • x * y

      :乘法
    • x / y

      :除法
    • x % y

      :求餘
  • 加法
    • x + y

      :加法、字元串串聯、委托組合
    • x – y

      :減法、委托删除
  • Shift
    • x << y

      :左移位
    • x >> y

      :右移位
  • 關系和類型測試
    • x < y

      :小于
    • x > y

      :大于
    • x <= y

      :小于或等于
    • x >= y

      :大于或等于
    • x is T

      :如果 

      x

       是 

      T

      ,傳回 

      true

      ;否則,傳回 

      false

    • x as T

      :傳回類型為 

      T

       的 

      x

      ;如果 

      x

       的類型不是 

      T

      ,傳回 

      null

  • 相等
    • x == y

      :等于
    • x != y

      :不等于
  • 邏輯“與”
    • x & y

      :整數型位AND,布爾型邏輯 AND
  • 邏輯 XOR
    • x ^ y

      :整數型位 XOR,布爾型邏輯 XOR
  • 邏輯“或”
    • x | y

      :整數型位 OR,布爾型邏輯 OR
  • 條件“與”
    • x && y

      :僅當 

      x

       不是 

      false

       時,才計算 

      y

  • 條件“或”
    • x || y

      :僅當 

      x

       不是 

      true

       時,才計算 

      y

  • null 合并
    • x ?? y

      :如果 

      x

       為 null,計算結果為 

      y

      ;否則,計算結果為 

      x

  • 條件運算
    • x ? y : z

      :如果 

      x

       為 

      true

      ,計算 

      y

      ;如果 

      x

       為 

      false

      ,計算 

      z

  • 指派或匿名函數
    • x = y

      :指派
    • x op= y

      :複合指派;支援以下運算符
      • *=

        /=

        %=

        +=

        -=

        <<=

        >>=

        &=

        ^=

        |=

    • (T x) => y

      :匿名函數(lambda 表達式)

語句

程式操作使用語句進行表示。 

C# 支援幾種不同的語句,其中許多語句是從嵌入語句的角度來定義的。

使用代碼塊,可以在允許編寫一個語句的上下文中編寫多個語句。 

代碼塊是由一系列在分隔符 

{

 和 

}

内編寫的語句組成。

聲明語句用于聲明局部變量和常量。

表達式語句用于計算表達式。 

可用作語句的表達式包括方法調用、使用 

new

 運算符的對象配置設定、使用 

=

和複合指派運算符的指派、使用 

++

 和 

--

 運算符和 

await

 表達式的遞增和遞減運算。

選擇語句用于根據一些表達式的值從多個可能的語句中選擇一個以供執行。 

這一類語句包括 

if

 和 

switch

 語句。

疊代語句用于重複執行嵌入語句。 

這一類語句包括 

while

do

for

 和 

foreach

 語句。

跳轉語句用于轉移控制權。 

這一類語句包括 

break

continue

goto

throw

return

 和 

yield

 語句。

try

...

catch

 語句用于捕獲在代碼塊執行期間發生的異常,

try

...

finally

 語句用于指定始終執行的最終代碼,無論異常發生與否。

checked

 和 

unchecked

 語句用于控制整型類型算術運算和轉換的溢出檢查上下文。

lock

 語句用于擷取給定對象的互相排斥鎖定,執行語句,然後解除鎖定。

using

 語句用于擷取資源,執行語句,然後釋放資源。

下面列出了可以使用的各種語句,以及每種語句的示例。

  • 局部變量聲明:
static void Declarations(string[] args)
{
    int a;
    int b = 2, c = 3;
    a = 1;
    Console.WriteLine(a + b + c);
}
           
  • 局部常量聲明:
static void ConstantDeclarations(string[] args)
{
    const float pi = 3.1415927f;
    const int r = 25;
    Console.WriteLine(pi * r * r);
}
           
  • 表達式語句:
static void Expressions(string[] args)
{
    int i;
    i = 123;                // Expression statement
    Console.WriteLine(i);   // Expression statement
    i++;                    // Expression statement
    Console.WriteLine(i);   // Expression statement
}
           
  • if

     語句:
static void IfStatement(string[] args)
{
    if (args.Length == 0)
    {
        Console.WriteLine("No arguments");
    }
    else 
    {
        Console.WriteLine("One or more arguments");
    }
}
           
  • switch

     語句:
static void SwitchStatement(string[] args)
{
    int n = args.Length;
    switch (n) 
    {
        case 0:
            Console.WriteLine("No arguments");
            break;
        case 1:
            Console.WriteLine("One argument");
            break;
        default:
            Console.WriteLine($"{n} arguments");
            break;
    }
}
           
  • while

     語句:
static void WhileStatement(string[] args)
{
    int i = 0;
    while (i < args.Length) 
    {
        Console.WriteLine(args[i]);
        i++;
    }
}
           
  • do

     語句:
static void DoStatement(string[] args)
{
    string s;
    do 
    {
        s = Console.ReadLine();
        Console.WriteLine(s);
    } while (!string.IsNullOrEmpty(s));
}

           
  • for

     語句:
static void ForStatement(string[] args)
{
    for (int i = 0; i < args.Length; i++) 
    {
        Console.WriteLine(args[i]);
    }
}
           
  • foreach

     語句:
static void ForEachStatement(string[] args)
{
    foreach (string s in args) 
    {
        Console.WriteLine(s);
    }
}
           
  • break

     語句:
static void BreakStatement(string[] args)
{
    while (true) 
    {
        string s = Console.ReadLine();
        if (string.IsNullOrEmpty(s)) 
            break;
        Console.WriteLine(s);
    }
}
           
  • continue

     語句:
static void ContinueStatement(string[] args)
{
    for (int i = 0; i < args.Length; i++) 
    {
        if (args[i].StartsWith("/")) 
            continue;
        Console.WriteLine(args[i]);
    }
}
           
  • goto

     語句:
static void GoToStatement(string[] args)
{
    int i = 0;
    goto check;
    loop:
    Console.WriteLine(args[i++]);
    check:
    if (i < args.Length) 
        goto loop;
}
           
  • return

     語句:
static int Add(int a, int b) 
{
    return a + b;
}
static void ReturnStatement(string[] args)
{
   Console.WriteLine(Add(1, 2));
   return;
}
           
  • yield

     語句:
static IEnumerable<int> Range(int from, int to) 
{
    for (int i = from; i < to; i++) 
    {
        yield return i;
    }
    yield break;
}
static void YieldStatement(string[] args)
{
    foreach (int i in Range(-10,10)) 
    {
        Console.WriteLine(i);
    }
}
           
  • throw

     和 

    try

     語句:
static double Divide(double x, double y) 
{
    if (y == 0) 
        throw new DivideByZeroException();
    return x / y;
}
static void TryCatch(string[] args) 
{
    try 
    {
        if (args.Length != 2) 
        {
            throw new InvalidOperationException("Two numbers required");
        }
        double x = double.Parse(args[0]);
        double y = double.Parse(args[1]);
        Console.WriteLine(Divide(x, y));
    }
    catch (InvalidOperationException e) 
    {
        Console.WriteLine(e.Message);
    }
    finally 
    {
        Console.WriteLine("Good bye!");
    }
}
           
  • checked

     和 

    unchecked

     語句:
static void CheckedUnchecked(string[] args) 
{
    int x = int.MaxValue;
    unchecked 
    {
        Console.WriteLine(x + 1);  // Overflow
    }
    checked 
    {
        Console.WriteLine(x + 1);  // Exception
    }     
}
           
  • lock

     語句:
class Account
{
    decimal balance;
    private readonly object sync = new object();
    public void Withdraw(decimal amount) 
    {
        lock (sync) 
        {
            if (amount > balance) 
            {
                throw new Exception(
                    "Insufficient funds");
            }
            balance -= amount;
        }
    }
}
           
  • using

     語句:
static void UsingStatement(string[] args) 
{
    using (TextWriter w = File.CreateText("test.txt")) 
    {
        w.WriteLine("Line one");
        w.WriteLine("Line two");
        w.WriteLine("Line three");
    }
}
           

類和對象

類是最基本的 C# 類型。 

類是一種資料結構,可在一個單元中就将狀态(字段)和操作(方法和其他函數成員)結合起來。 

類為動态建立的類執行個體(亦稱為“對象”)提供了定義。 

類支援繼承和多形性,即派生類可以擴充和專門針對基類的機制。

新類使用類聲明進行建立。 類聲明的開頭是标頭,指定了類的特性和修飾符、類名、基類(若指定)以及類實作的接口。 

标頭後面是類主體,由在分隔符 

{

 和 

}

 内編寫的成員聲明清單組成。

以下是簡單類 

Point

 的聲明:

public class Point
{
    public int x, y;
    public Point(int x, int y) 
    {
        this.x = x;
        this.y = y;
    }
}
           

類執行個體是使用 

new

 運算符進行建立,此運算符為新執行個體配置設定記憶體,調用構造函數來初始化執行個體,并傳回對執行個體的引用。 

以下語句建立兩個 Point 對象,并将對這些對象的引用存儲在兩個變量中:

Point p1 = new Point(0, 0);
Point p2 = new Point(10, 20);
           

當無法再通路對象時,對象占用的記憶體會被自動回收。 既沒必要,也無法在 C# 中顯式解除配置設定對象。

成員

類成員要麼是靜态成員,要麼是執行個體成員。 靜态成員屬于類,而執行個體成員則屬于對象(類執行個體)。

下面概述了類可以包含的成員類型。

  • 常量
    • 與類相關聯的常量值
  • 字段
    • 類的常量
  • 方法
    • 類可以執行的計算和操作
  • 屬性
    • 與讀取和寫入類的已命名屬性相關聯的操作
  • 索引器
    • 與将類執行個體編入索引(像處理數組一樣)相關聯的操作
  • 事件
    • 類可以生成的通知
  • 運算符
    • 類支援的轉換和表達式運算符
  • 構造函數
    • 初始化類執行個體或類本身所需的操作
  • 終結器
    • 永久放棄類執行個體前要執行的操作
  • 類型
    • 類聲明的嵌套類型

可通路性

每個類成員都有關聯的可通路性,用于控制能夠通路成員的程式文本區域。 可通路性有六種可能的形式。總結如下。

  • public

    • 通路不受限
  • protected

    • 隻能通路此類或派生自此類的類
  • internal

    • 通路限于目前程式集(.exe、.dll 等)
  • protected internal

    • 通路限于包含類、派生自包含類的類或同一程式集中的類
  • private

    • 隻能通路此類
  • private protected

    • 通路限于同一程式集中的包含類或派生自包含類的類

類型參數

類定義可能會按如下方式指定一組類型參數:在類名後面用尖括号包覆類型參數名稱清單。 

然後,可以在類聲明的主體中使用類型參數來定義類成員。 

在以下示例中,

Pair

 的類型參數是 

TFirst

 和 

TSecond

public class Pair<TFirst,TSecond>
{
    public TFirst First;
    public TSecond Second;
}
           

聲明為需要使用類型參數的類類型被稱為泛型類類型。 

結構、接口和委托類型也可以是泛型。 使用泛型類時,必須為每個類型參數提供類型自變量:

Pair<int,string> pair = new Pair<int,string> { First = 1, Second = "two" };
int i = pair.First;     // TFirst is int
string s = pair.Second; // TSecond is string
           

包含類型自變量的泛型類型(如上面的 

Pair<int,string>

)被稱為構造泛型類型。

基類

類聲明可能會按如下方式指定基類:在類名和類型參數後面編寫冒号和基類名。 省略基類規範與從 

object

 類型派生相同。 在以下示例中,

Point3D

 的基類是 

Point

Point

 的基類是 

object

public class Point
{
    public int x, y;
    public Point(int x, int y) 
    {
        this.x = x;
        this.y = y;
    }
}
public class Point3D: Point
{
    public int z;
    public Point3D(int x, int y, int z) : 
        base(x, y) 
    {
        this.z = z;
    }
}
           

類繼承其基類的成員。 繼承是指隐式包含其基類的所有成員的類,執行個體和靜态構造函數以及基類的終結器除外。 派生類可以其繼承的類添加新成員,但無法删除繼承成員的定義。 在上面的示例中,

Point3D

 從 

Point

 繼承了 

x

 和 

y

 字段,每個 

Point3D

 執行個體均包含三個字段(

x

y

 和 

z

)。

可以将類類型隐式轉換成其任意基類類型。 是以,類類型的變量可以引用相應類的執行個體或任意派生類的執行個體。 例如,類聲明如上,

Point

 類型的變量可以引用 

Point

 或 

Point3D

Point a = new Point(10, 20);
Point b = new Point3D(10, 20, 30);
           

字段

字段是與類或類執行個體相關聯的變量。

使用靜态修飾符聲明的字段定義的是靜态字段。 靜态字段隻指明一個存儲位置。 無論建立多少個類執行個體,永遠隻有一個靜态字段副本。

不使用靜态修飾符聲明的字段定義的是執行個體字段。 每個類執行個體均包含相應類的所有執行個體字段的單獨副本。

在以下示例中,每個 

Color

 類執行個體均包含 

r

g

 和 

b

 執行個體字段的單獨副本,但分别隻包含 

Black

White

Red

Green

 和 

Blue

 靜态字段的一個副本:

public class Color
{
    public static readonly Color Black = new Color(0, 0, 0);
    public static readonly Color White = new Color(255, 255, 255);
    public static readonly Color Red = new Color(255, 0, 0);
    public static readonly Color Green = new Color(0, 255, 0);
    public static readonly Color Blue = new Color(0, 0, 255);
    private byte r, g, b;
    public Color(byte r, byte g, byte b) 
    {
        this.r = r;
        this.g = g;
        this.b = b;
    }
}
           

如上面的示例所示,可以使用 

readonly

 修飾符聲明隻讀字段。 隻能在字段聲明期間或在同一個類的構造函數中向 

readonly

 字段指派。

方法

方法是實作對象或類可執行的計算或操作的成員。 靜态方法是通過類進行通路。 執行個體方法是通過類執行個體進行通路。

方法可能具有參數清單,用于表示傳遞給方法的值或變量引用;并具有傳回類型,用于指定方法計算并傳回的值的類型。 如果方法未傳回值,則其傳回類型為 

void

方法可能也包含一組類型參數,必須在調用方法時指定類型自變量,這一點與類型一樣。 與類型不同的是,通常可以根據方法調用的自變量推斷出類型自變量,無需顯式指定。

在聲明方法的類中,方法的簽名必須是唯一的。 方法簽名包含方法名稱、類型參數數量及其參數的數量、修飾符和類型。 方法簽名不包含傳回類型。

參數

參數用于将值或變量引用傳遞給方法。 方法參數從調用方法時指定的自變量中擷取其實際值。 有四類參數:值參數、引用參數、輸出參數和參數數組。

值參數用于傳遞輸入自變量。 值參數對應于局部變量,從為其傳遞的自變量中擷取初始值。 修改值參數不會影響為其傳遞的自變量。

可以指定預設值,進而省略相應的自變量,這樣值參數就是可選的。

引用參數用于按引用傳遞自變量。 為引用參數傳遞的自變量必須是具有明确值的變量,并且在方法執行期間,引用參數指明的存儲位置與自變量相同。 引用參數使用 

ref

 修飾符進行聲明。 下面的示例展示了如何使用 

ref

 參數。

using System;
class RefExample
{
    static void Swap(ref int x, ref int y) 
    {
        int temp = x;
        x = y;
        y = temp;
    }
    public static void SwapExample() 
    {
        int i = 1, j = 2;
        Swap(ref i, ref j);
        Console.WriteLine($"{i} {j}");    // Outputs "2 1"
    }
}
           

輸出參數用于按引用傳遞自變量。 輸出參數與引用參數類似,不同之處在于,不要求向調用方提供的自變量顯式指派。 輸出參數使用 

out

 修飾符進行聲明。 下面的示例示範如何通過 C# 7 中引入的文法使用 

out

 參數。

using System;
    class OutExample
    {
        static void Divide(int x, int y, out int result, out int remainder) 
        {
            result = x / y;
            remainder = x % y;
        }
        public static void OutUsage() 
        {
            Divide(10, 3, out int res, out int rem);
            Console.WriteLine("{0} {1}", res, rem);	// Outputs "3 1"
        }
    }
}
           

參數數組允許向方法傳遞數量不定的自變量。 參數數組使用 

params

 修飾符進行聲明。 參數數組隻能是方法的最後一個參數,且參數數組的類型必須是一維數組類型。 System.Console 類的 Write 和 WriteLine 方法是參數數組用法的典型示例。 它們的聲明方式如下。

public class Console
{
    public static void Write(string fmt, params object[] args) { }
    public static void WriteLine(string fmt, params object[] args) { }
    // ...
}
           

在使用參數數組的方法中,參數數組的行為與數組類型的正常參數完全相同。 不過,在調用包含參數數組的方法時,要麼可以傳遞參數數組類型的一個自變量,要麼可以傳遞參數數組的元素類型的任意數量自變量。 在後一種情況中,數組執行個體會自動建立,并初始化為包含給定的自變量。 以下示例:

Console.WriteLine("x={0} y={1} z={2}", x, y, z);
           

等同于編寫以下代碼:

string s = "x={0} y={1} z={2}";
object[] args = new object[3];
args[0] = x;
args[1] = y;
args[2] = z;
Console.WriteLine(s, args);
           

方法主體和局部變量

方法主體指定了在調用方法時執行的語句。

方法主體可以聲明特定于方法調用的變量。 此類變量稱為局部變量。 局部變量聲明指定了類型名稱、變量名稱以及可能的初始值。 下面的示例聲明了初始值為零的局部變量 

i

 和無初始值的局部變量 

j

using System;
class Squares
{
    public static void WriteSquares() 
    {
        int i = 0;
        int j;
        while (i < 10) 
        {
            j = i * i;
            Console.WriteLine($"{i} x {i} = {j}");
            i = i + 1;
        }
    }
}
           

C# 要求必須先明确指派局部變量,然後才能擷取其值。 例如,如果上面的 

i

 聲明未包含初始值,那麼編譯器會在後面使用 

i

 時報告錯誤,因為在後面使用時 

i

 不會在程式中進行明确指派。

方法可以使用 

return

 語句将控制權傳回給調用方。 在傳回 

void

 的方法中,

return

 語句無法指定表達式。 在不傳回 void 的方法中,

return

 語句必須包括用于計算傳回值的表達式。

靜态和執行個體方法

使用靜态修飾符聲明的方法是靜态方法。 靜态方法不對特定的執行個體起作用,隻能直接通路靜态成員。

不使用靜态修飾符聲明的方法是執行個體方法。 執行個體方法對特定的執行個體起作用,并能夠通路靜态和執行個體成員。其中調用執行個體方法的執行個體可以作為 

this

 顯式通路。 在靜态方法中引用 

this

 會生成錯誤。

以下 

Entity

 類包含靜态和執行個體成員。

class Entity
{
    static int nextSerialNo;
    int serialNo;
    public Entity() 
    {
        serialNo = nextSerialNo++;
    }
    public int GetSerialNo() 
    {
        return serialNo;
    }
    public static int GetNextSerialNo() 
    {
        return nextSerialNo;
    }
    public static void SetNextSerialNo(int value) 
    {
        nextSerialNo = value;
    }
}
           

每個 

Entity

 執行個體均有一個序列号(很可能包含此處未顯示的其他一些資訊)。 

Entity

 構造函數(類似于執行個體方法)将新執行個體初始化為包含下一個可用的序列号。 由于構造函數是執行個體成員,是以可以通路 

serialNo

 執行個體字段和 

nextSerialNo

 靜态字段。

GetNextSerialNo

 和 

SetNextSerialNo

 靜态方法可以通路 

nextSerialNo

 靜态字段,但如果直接通路 

serialNo

 執行個體字段,則會生成錯誤。

下面的示例展示了如何使用 Entity 類。

using System;
class EntityExample
{
    public static void Usage() 
    {
        Entity.SetNextSerialNo(1000);
        Entity e1 = new Entity();
        Entity e2 = new Entity();
        Console.WriteLine(e1.GetSerialNo());            // Outputs "1000"
        Console.WriteLine(e2.GetSerialNo());            // Outputs "1001"
        Console.WriteLine(Entity.GetNextSerialNo());    // Outputs "1002"
    }
}
           

請注意,

SetNextSerialNo

 和 

GetNextSerialNo

 靜态方法是在類中調用,而 

GetSerialNo

 執行個體方法則是在類執行個體中調用。

虛方法、重寫方法和抽象方法 (asdf)

如果執行個體方法聲明中有 

virtual

 修飾符,可以将執行個體方法稱為“虛方法”。 如果沒有 virtual 修飾符,可以将執行個體方法稱為“非虛方法”。

調用虛方法時,為其調用方法的執行個體的運作時類型決定了要調用的實際方法實作代碼。 調用非虛方法時,執行個體的編譯時類型是決定性因素。

可以在派生類中重寫虛方法。 如果執行個體方法聲明中有 override 修飾符,那麼執行個體方法可以重寫簽名相同的繼承虛方法。 但如果虛方法聲明中引入新方法,重寫方法聲明通過提供相應方法的新實作代碼,專門針對現有的繼承虛方法。

抽象方法是沒有實作代碼的虛方法。 抽象方法使用 abstract 修飾符進行聲明,隻能在同樣聲明了 abstract 的類中使用。 必須在所有非抽象派生類中重寫抽象方法。

下面的示例聲明了一個抽象類 

Expression

,用于表示表達式樹節點;還聲明了三個派生類(

Constant

VariableReference

 和 

Operation

),用于實作常量、變量引用和算術運算的表達式樹節點。 (這與表達式樹類型相似,但不能與之混淆)。

using System;
using System.Collections.Generic;
public abstract class Expression
{
    public abstract double Evaluate(Dictionary<string,object> vars);
}
public class Constant: Expression
{
    double value;
    public Constant(double value) 
    {
        this.value = value;
    }
    public override double Evaluate(Dictionary<string,object> vars) 
    {
        return value;
    }
}
public class VariableReference: Expression
{
    string name;
    public VariableReference(string name) 
    {
        this.name = name;
    }
    public override double Evaluate(Dictionary<string,object> vars) 
    {
        object value = vars[name];
        if (value == null) 
        {
            throw new Exception("Unknown variable: " + name);
        }
        return Convert.ToDouble(value);
    }
}
public class Operation: Expression
{
    Expression left;
    char op;
    Expression right;
    public Operation(Expression left, char op, Expression right) 
    {
        this.left = left;
        this.op = op;
        this.right = right;
    }
    public override double Evaluate(Dictionary<string,object> vars) 
    {
        double x = left.Evaluate(vars);
        double y = right.Evaluate(vars);
        switch (op) {
            case '+': return x + y;
            case '-': return x - y;
            case '*': return x * y;
            case '/': return x / y;
        }
        throw new Exception("Unknown operator");
    }
}
           

上面的四個類可用于進行算術表達式模組化。 例如,使用這些類的執行個體,可以按如下方式表示表達式 

x + 3

Expression e = new Operation(
    new VariableReference("x"),
    '+',
    new Constant(3));
           

調用 

Expression

 執行個體的 

Evaluate

 方法可以計算給定的表達式并生成 

double

 值。 此方法需要使用自變量 

Dictionary

,其中包含變量名稱(作為項鍵)和值(作為項值)。 因為 

Evaluate

 是一個抽象方法,是以派生自 

Expression

 的非抽象類必須替代 

Evaluate

Constant

 的 

Evaluate

 實作代碼隻傳回存儲的常量。 

VariableReference

 實作代碼查找字典中的變量名稱,并傳回結果值。 

Operation

 實作代碼先計算左右操作數(以遞歸方式調用其 

Evaluate

 方法),然後執行給定的算術運算。

以下程式使用 

Expression

 類根據不同的 

x

 和 

y

 值計算表達式 

x * (y + 2)

using System;
using System.Collections.Generic;
class InheritanceExample
{
    public static void ExampleUsage() 
    {
        Expression e = new Operation(
            new VariableReference("x"),
            '*',
            new Operation(
                new VariableReference("y"),
                '+',
                new Constant(2)
            )
        );
        Dictionary<string,object> vars = new Dictionary<string, object>();
        vars["x"] = 3;
        vars["y"] = 5;
        Console.WriteLine(e.Evaluate(vars));		// Outputs "21"
        vars["x"] = 1.5;
        vars["y"] = 9;
        Console.WriteLine(e.Evaluate(vars));		// Outputs "16.5"
    }
}   
           

方法重載

借助方法重載,同一類中可以有多個同名的方法,隻要這些方法具有唯一簽名即可。 編譯如何調用重載的方法時,編譯器使用重載決策來确定要調用的特定方法。 重載決策查找與自變量最比對的方法;如果找不到最佳比對項,則會報告錯誤。 下面的示例展示了重載決策的實際工作方式。 

UsageExample

 方法中每個調用的注釋指明了實際調用的方法。

using System;
class OverloadingExample
{
    static void F() 
    {
        Console.WriteLine("F()");
    }
    static void F(object x) 
    {
        Console.WriteLine("F(object)");
    }
    static void F(int x) 
    {
        Console.WriteLine("F(int)");
    }
    static void F(double x) 
    {
        Console.WriteLine("F(double)");
    }
    static void F<T>(T x) 
    {
        Console.WriteLine("F<T>(T)");
    }
    static void F(double x, double y) 
    {
        Console.WriteLine("F(double, double)");
    }
    public static void UsageExample()
    {
        F();            // Invokes F()
        F(1);           // Invokes F(int)
        F(1.0);         // Invokes F(double)
        F("abc");       // Invokes F<string>(string)
        F((double)1);   // Invokes F(double)
        F((object)1);   // Invokes F(object)
        F<int>(1);      // Invokes F<int>(int)
        F(1, 1);        // Invokes F(double, double)
    }
}
           

如示例所示,可以随時将自變量顯式轉換成确切的參數類型,并/或顯式提供類型自變量,進而選擇特定的方法。

其他函數成員

包含可執行代碼的成員統稱為類的函數成員。 上一部分介紹了作為主要函數成員類型的方法。 此部分将介紹 C# 支援的其他類型函數成員:構造函數、屬性、索引器、事件、運算符和終結器。

下面的示例展示了 List 泛型類,用于實作對象的可擴充清單。 此類包含最常見類型函數成員的多個示例。

public class List<T>
{
    // Constant
    const int defaultCapacity = 4;

    // Fields
    T[] items;
    int count;

    // Constructor
    public List(int capacity = defaultCapacity) 
    {
        items = new T[capacity];
    }

    // Properties
    public int Count => count; 

    public int Capacity 
    {
        get { return items.Length; }
        set 
        {
            if (value < count) value = count;
            if (value != items.Length) 
            {
                T[] newItems = new T[value];
                Array.Copy(items, 0, newItems, 0, count);
                items = newItems;
            }
        }
    }

    // Indexer
    public T this[int index] 
    {
        get 
        {
            return items[index];
        }
        set 
        {
            items[index] = value;
            OnChanged();
        }
    }
    
    // Methods
    public void Add(T item) 
    {
        if (count == Capacity) Capacity = count * 2;
        items[count] = item;
        count++;
        OnChanged();
    }
    protected virtual void OnChanged() =>
        Changed?.Invoke(this, EventArgs.Empty);

    public override bool Equals(object other) =>
        Equals(this, other as List<T>);

    static bool Equals(List<T> a, List<T> b) 
    {
        if (Object.ReferenceEquals(a, null)) return Object.ReferenceEquals(b, null);
        if (Object.ReferenceEquals(b, null) || a.count != b.count)
            return false;
        for (int i = 0; i < a.count; i++) 
        {
            if (!object.Equals(a.items[i], b.items[i])) 
            {
                return false;
            }
        }
    return true;
    }

    // Event
    public event EventHandler Changed;

    // Operators
    public static bool operator ==(List<T> a, List<T> b) => 
        Equals(a, b);

    public static bool operator !=(List<T> a, List<T> b) => 
        !Equals(a, b);
}
           

構造函數

C# 支援執行個體和靜态構造函數。 執行個體構造函數是實作初始化類執行個體所需執行的操作的成員。 靜态構造函數是實作在首次加載類時初始化類本身所需執行的操作的成員。

構造函數的聲明方式與方法一樣,都沒有傳回類型,且與所含類同名。 如果構造函數聲明包含靜态修飾符,則聲明的是靜态構造函數。 否則,聲明的是執行個體構造函數。

執行個體構造函數可以重載,并能包含可選參數。 例如,

List<T>

 類聲明兩個執行個體構造函數:一個沒有參數,另一個需要使用 

int

 參數。 執行個體構造函數使用 

new

 運算符進行調用。 下面的語句使用包含和不包含可選自變量的 

List

 類構造函數來配置設定兩個 

List<string>

 執行個體。

List<string> list1 = new List<string>();
List<string> list2 = new List<string>(10);
           

與其他成員不同,執行個體構造函數不能被繼承,且類中隻能包含實際已聲明的執行個體構造函數。 如果沒有為類提供執行個體構造函數,則會自動提供不含參數的空執行個體構造函數。

屬性

屬性是字段的自然擴充。 兩者都是包含關聯類型的已命名成員,用于通路字段和屬性的文法也是一樣的。不過,與字段不同的是,屬性不指明存儲位置。 相反,屬性包含通路器,用于指定在讀取或寫入屬性值時要執行的語句。

屬性的聲明方式與字段類似,不同之處在于,屬性聲明以在分隔符 

{

 和 

}

 内寫入的 get 通路器和/或 set 通路器結束,而不是以分号結束。 同時包含 get 通路器和 set 通路器的屬性是讀寫屬性,僅包含 get 通路器的屬性是隻讀屬性,僅包含 set 通路器的屬性是隻寫屬性。

get 通路器對應于包含屬性類型的傳回值的無參數方法。 如果在表達式中引用屬性,除了作為指派目标以外,調用的屬性 get 通路器還可用于計算屬性值。

set 通路器對應于包含一個名為 value 的參數但不含傳回類型的方法。 如果将屬性引用為指派目标或 ++/-- 的操作數,将調用 set 通路器(由自變量提供新值)。

List<T>

 類聲明以下兩個屬性:Count 和 Capacity(分别是隻讀和隻寫屬性)。 下面的示例展示了如何使用這些屬性。

List<string> names = new List<string>();
names.Capacity = 100;   // Invokes set accessor
int i = names.Count;    // Invokes get accessor
int j = names.Capacity; // Invokes get accessor
           

類似于字段和方法,C# 支援執行個體屬性和靜态屬性。 靜态屬性使用靜态修飾符進行聲明,而執行個體屬性則不使用靜态修飾符進行聲明。

屬性的通路器可以是虛的。 如果屬性聲明包含 

virtual

abstract

 或 

override

 修飾符,則适用于屬性的通路器。

索引器

借助索引器成員,可以将對象編入索引(像處理數組一樣)。 索引器的聲明方式與屬性類似,不同之處在于,索引器成員名稱格式為後跟分隔符 

[

 和 

]

,其中寫入參數清單。 這些參數在索引器的通路器中可用。 類似于屬性,索引器分為讀寫、隻讀和隻寫索引器,且索引器的通路器可以是虛的。

List

 類聲明一個需要使用 

int

 參數的讀寫索引器。 借助索引器,可以使用 

int

 值将 

List

 執行個體編入索引。 例如:

List<string> names = new List<string>();
names.Add("Liz");
names.Add("Martha");
names.Add("Beth");
for (int i = 0; i < names.Count; i++) 
{
    string s = names[i];
    names[i] = s.ToUpper();
}
           

索引器可以進行重載。也就是說,類可以聲明多個索引器,隻要其參數的數量或類型不同即可。

事件

借助事件成員,類或對象可以提供通知。 事件的聲明方式與字段類似,不同之處在于,事件聲明包括事件關鍵字,且類型必須是委托類型。

在聲明事件成員的類中,事件的行為與委托類型的字段完全相同(前提是事件不是抽象的,且不聲明通路器)。 字段存儲對委托的引用,委托表示已添加到事件的事件處理程式。 如果沒有任何事件處理程式,則字段為 

null

List<T>

 類聲明一個 

Changed

 事件成員,指明已向清單添加了新項。 Changed 事件由 

OnChanged

 虛方法引發,此方法會先檢查事件是否是 

null

(即不含任何處理程式)。 引發事件的概念恰恰等同于調用由事件表示的委托,是以,沒有用于引發事件的特殊語言構造。

用戶端通過事件處理程式響應事件。 使用 

+=

 和 

-=

 運算符分别可以附加和删除事件處理程式。 下面的示例展示了如何向 

List<string>

 的 

Changed

 事件附加事件處理程式。

class EventExample
{
    static int changeCount;
    static void ListChanged(object sender, EventArgs e) 
    {
        changeCount++;
    }
    public static void Usage() 
    {
        List<string> names = new List<string>();
        names.Changed += new EventHandler(ListChanged);
        names.Add("Liz");
        names.Add("Martha");
        names.Add("Beth");
        Console.WriteLine(changeCount);		// Outputs "3"
    }
}
           

對于需要控制事件的基礎存儲的進階方案,事件聲明可以顯式提供 

add

 和 

remove

 通路器,這在某種程度上與屬性的 

set

 通路器類似。

運算符

運算符是定義向類執行個體應用特定表達式運算符的含義的成員。 可以定義三種類型的運算符:一進制運算符、二進制運算符和轉換運算符。 所有運算符都必須聲明為 

public

 和 

static

List<T>

 類聲明兩個運算符(

operator ==

 和 

operator !=

),是以定義了向 

List

 執行個體應用這些運算符的表達式的新含義。 具體而言,這些運算符定義的是兩個 

List<T>

 執行個體的相等性(使用其 Equals 方法比較所包含的每個對象)。 下面的示例展示了如何使用 

==

 運算符比較兩個 

List<int>

 執行個體。

List<int> a = new List<int>();
a.Add(1);
a.Add(2);
List<int> b = new List<int>();
b.Add(1);
b.Add(2);
Console.WriteLine(a == b);  // Outputs "True" 
b.Add(3);
Console.WriteLine(a == b);  // Outputs "False"
           

第一個 

Console.WriteLine

 輸出 

True

,因為兩個清單包含的對象不僅數量相同,而且值和順序也相同。如果 

List<T>

 未定義 

operator ==

,那麼第一個 

Console.WriteLine

 會輸出 

False

,因為 

a

 和 

b

 引用不同的 

List<int>

 執行個體。

終結器

終結器是實作完成類執行個體所需的操作的成員。 終結器既不能包含參數和可通路性修飾符,也不能進行顯式調用。 執行個體的終結器在垃圾回收期間自動調用。

垃圾回收器在決定何時收集對象和運作終結器時有很大自由度。 具體來說,終結器的調用時間具有不确定性,可以在任意線程上執行終結器。 因為這樣或那樣的原因,隻有在沒有其他可行的解決方案時,類才能實作終結器。

處理對象析構的更好方法是使用 

using

 語句。

結構

結構是可以包含資料成員和函數成員的資料結構,這一點與類一樣;與類不同的是,結構是值類型,無需進行堆配置設定。 結構類型的變量直接存儲結構資料,而類類型的變量存儲對動态配置設定的對象的引用。 結構類型不支援使用者指定的繼承,并且所有結構類型均隐式繼承自類型 ValueType,後者又隐式繼承自 

object

結構對包含值語義的小型資料結構特别有用。 複數、坐标系中的點或字典中的鍵值對都是結構的典型示例。 對小型資料結構使用結構(而不是類)在應用程式執行的記憶體配置設定次數上存在巨大差異。 例如,以下程式建立并初始化包含 100 個點的數組。 通過将 

Point

 實作為類,可單獨執行個體化 101 個對象,一個對象用于數組,其他所有對象分别用于 100 個元素。

public class PointExample
{
    public static void Main() 
    {
        Point[] points = new Point[100];
        for (int i = 0; i < 100; i++)
            points[i] = new Point(i, i);
    }
}
           

也可以選擇将 Point 實作為結構。

struct Point
{
    public int x, y;
    public Point(int x, int y) 
    {
        this.x = x;
        this.y = y;
    }
}
           

現在,僅執行個體化一個對象(即用于數組的對象),

Point

 執行個體存儲内嵌在數組中。

結構構造函數使用 

new

 運算符進行調用,但這不并表示要配置設定記憶體。 結構構造函數隻傳回結構值本身(通常在堆棧的臨時位置中),并在必要時複制此值,而非動态配置設定對象并傳回對此對象的引用。

借助類,兩個變量可以引用同一對象;是以,對一個變量執行的運算可能會影響另一個變量引用的對象。借助結構,每個變量都有自己的資料副本;是以,對一個變量執行的運算不會影響另一個變量。 例如,以下代碼片段生成的輸出取決于 Point 是類還是結構。

Point a = new Point(10, 10);
Point b = a;
a.x = 20;
Console.WriteLine(b.x);
           

如果 

Point

 是類,則輸出 20,因為 a 和 b 引用同一對象。 如果 Point 是結構,則輸出 10,因為将 

a

 指派給 

b

 建立了值副本,而此副本不受後面對 

a.x

 的指派的影響。

以上示例突出顯示了結構的兩個限制。 首先,複制整個結構通常比複制對象引用效率更低,是以通過結構進行的指派和值參數傳遞可能比通過引用類型成本更高。 其次,除 

in

ref

 和 

out

 參數以外,無法建立對結構的引用,這就表示在很多應用場景中都不能使用結構。

數組

數組是一種資料結構,其中包含許多通過計算索引通路的變量。 數組中的變量(亦稱為數組的元素)均為同一種類型,我們将這種類型稱為數組的元素類型。

數組類型是引用類型,聲明數組變量隻是為引用數組執行個體預留白間。 實際的數組執行個體是在運作時使用 new 運算符動态建立而成。 new 運算指定了新數組執行個體的長度,然後在此執行個體的生存期内固定使用這個長度。數組元素的索引介于 

 到 

Length - 1

 之間。 

new

 運算符自動将數組元素初始化為其預設值(例如,所有數值類型的預設值為 0,所有引用類型的預設值為 

null

)。

以下示例建立 

int

 元素數組,初始化此數組,然後列印輸出此數組的内容。

using System;
class ArrayExample
{
    static void Main() 
    {
        int[] a = new int[10];
        for (int i = 0; i < a.Length; i++) 
        {
            a[i] = i * i;
        }
        for (int i = 0; i < a.Length; i++) 
        {
            Console.WriteLine($"a[{i}] = {a[i]}");
        }
    }
}
           

上面的示例建立一維數組,并對其執行運算。 C# 還支援多元數組。 數組類型的維數(亦稱為數組類型的秩)是 1 與數組類型方括号内的逗号數量相加的結果。 以下示例分别配置設定一維、二維、三維數組。

int[] a1 = new int[10];
int[,] a2 = new int[10, 5];
int[,,] a3 = new int[10, 5, 2];
           

a1

 數組包含 10 個元素,

a2

 數組包含 50 個元素 (10 × 5),

a3

 數組包含 100 個元素 (10 × 5 × 2)。 數組的元素類型可以是任意類型(包括數組類型)。 包含數組類型元素的數組有時稱為交錯數組,因為元素數組的長度不必全都一樣。 以下示例配置設定由 

int

 數組構成的數組:

int[][] a = new int[3][];
a[0] = new int[10];
a[1] = new int[5];
a[2] = new int[20];
           

第一行建立包含三個元素的數組,每個元素都是 

int[]

 類型,并且初始值均為 

null

。 後面的代碼行将這三個元素初始化為引用長度不同的各個數組執行個體。

通過 new 運算符,可以使用數組初始值設定項(在分隔符 

{

 和 

}

 内編寫的表達式清單)指定數組元素的初始值。 以下示例配置設定 

int[]

,并将其初始化為包含三個元素。

int[] a = new int[] {1, 2, 3};
           

請注意,可從 { 和 } 内的表達式數量推斷出數組的長度。 局部變量和字段聲明可以進一步縮短,這樣就不用重新聲明數組類型了。

int[] a = {1, 2, 3};
           

以上兩個示例等同于以下示例:

int[] t = new int[3];
t[0] = 1;
t[1] = 2;
t[2] = 3;
int[] a = t;
           

接口

接口定義了可由類和結構實作的協定。 接口可以包含方法、屬性、事件和索引器。 接口不提供所定義的成員的實作代碼,僅指定必須由實作接口的類或結構提供的成員。

接口可以采用多重繼承。 在以下示例中,接口 

IComboBox

 同時繼承自 

ITextBox

 和 

IListBox

interface IControl
{
    void Paint();
}
interface ITextBox: IControl
{
    void SetText(string text);
}
interface IListBox: IControl
{
    void SetItems(string[] items);
}
interface IComboBox: ITextBox, IListBox {}
           

類和結構可以實作多個接口。 在以下示例中,類 

EditBox

 同時實作 

IControl

 和 

IDataBound

interface IDataBound
{
    void Bind(Binder b);
}
public class EditBox: IControl, IDataBound
{
    public void Paint() { }
    public void Bind(Binder b) { }
} 
           

當類或結構實作特定接口時,此類或結構的執行個體可以隐式轉換成相應的接口類型。 例如

EditBox editBox = new EditBox();
IControl control = editBox;
IDataBound dataBound = editBox;
           

如果已知執行個體不是靜态地實作特定接口,可以使用動态類型顯式轉換功能。 例如,以下語句使用動态類型顯式轉換功能來擷取對象的 

IControl

 和 

IDataBound

 接口實作代碼。 因為對象的運作時實際類型是 

EditBox

,是以顯式轉換會成功。

object obj = new EditBox();
IControl control = (IControl)obj;
IDataBound dataBound = (IDataBound)obj;
           

在前面的 

EditBox

 類中,

IControl

 接口中的 

Paint

 方法和 

IDataBound

 接口中的 

Bind

 方法均使用公共成員進行實作。 C# 還支援顯式接口成員實作代碼,這樣類或結構就不會将成員設為公共成員。 顯式接口成員實作代碼是使用完全限定的接口成員名稱進行編寫。 例如,

EditBox

 類可以使用顯式接口成員實作代碼來實作 

IControl.Paint

 和 

IDataBound.Bind

 方法,如下所示。

public class EditBox: IControl, IDataBound
{
    void IControl.Paint() { }
    void IDataBound.Bind(Binder b) { }
}
           

顯式接口成員隻能通過接口類型進行通路。 例如,隻有先将 

EditBox

 引用轉換成 

IControl

 接口類型,才能調用上面 EditBox 類提供的 

IControl.Paint

 實作代碼。

EditBox editBox = new EditBox();
editBox.Paint();            // Error, no such method
IControl control = editBox;
control.Paint();            // Ok
           

枚舉

枚舉類型是包含一組已命名常量的獨特值類型。 需要定義包含一組離散值的類型時,可以定義枚舉。 枚舉使用一種整型值類型作為其基礎存儲, 并提供離散值的語義含義。

以下示例聲明并使用名為“

Color

”的 

enum

 類型,其中包含三個常量值(

Red

Green

 和 

Blue

)。

using System;
enum Color
{
    Red,
    Green,
    Blue
}
class EnumExample
{
    static void PrintColor(Color color) 
    {
        switch (color) 
        {
            case Color.Red:
                Console.WriteLine("Red");
                break;
            case Color.Green:
                Console.WriteLine("Green");
                break;
            case Color.Blue:
                Console.WriteLine("Blue");
                break;
            default:
                Console.WriteLine("Unknown color");
                break;
        }
    }
    static void Main() 
    {
        Color c = Color.Red;
        PrintColor(c);
        PrintColor(Color.Blue);
    }
}
           

每個 

enum

 類型都有對應的整型類型(稱為 

enum

 類型的基礎類型)。 如果 

enum

 類型未顯式聲明基礎類型,則基礎類型為 

int

。 

enum

 類型的存儲格式和可取值範圍由基礎類型決定。 

enum

 類型需要使用的一組值不受其 

enum

 成員限制。 尤其是,基礎類型的 

enum

 的任何值都可以顯式轉換成 

enum

 類型,并作為 

enum

 類型的不同有效值。

以下示例聲明基礎類型為 

sbyte

 且名為“

Alignment

”的 

enum

 類型。

enum Alignment: sbyte
{
    Left = -1,
    Center = 0,
    Right = 1
}
           

如上面的示例所示,

enum

 成員聲明可以包含用于指定成員值的常數表達式。 每個 

enum

 成員的常量值都必須介于 

enum

 的基礎類型範圍内。 如果 

enum

 成員聲明未顯式指定值,那麼會為成員指定值 0(如果是 

enum

 類型中的首個成員)或原文前一個 

enum

 成員的值加 1。

可使用類型顯式轉換功能将 

Enum

 值轉換成整型值,反之亦然。 例如:

int i = (int)Color.Blue;    // int i = 2;
Color c = (Color)2;         // Color c = Color.Blue;  
           

任何 

enum

 類型的預設值都是已轉換成 

enum

 類型的整型值 0。 如果變量被自動初始化為預設值,這就是為 

enum

 類型的變量指定的值。 為了讓 

enum

 類型的預設值可供友善使用,文本類型 

 隐式轉換成任意 

enum

 類型。 是以,可以運作以下指令。

Color c = 0;
           

委托

委托類型表示對具有特定參數清單和傳回類型的方法的引用。 通過委托,可以将方法視為可配置設定給變量并可作為參數傳遞的實體。 委托類似于其他一些語言中的函數指針概念,但與函數指針不同的是,委托不僅面向對象,還類型安全。

下面的示例聲明并使用 

Function

 委托類型。

using System;
delegate double Function(double x);
class Multiplier
{
    double factor;
    public Multiplier(double factor) 
    {
        this.factor = factor;
    }
    public double Multiply(double x) 
    {
        return x * factor;
    }
}
class DelegateExample
{
    static double Square(double x) 
    {
        return x * x;
    }
    static double[] Apply(double[] a, Function f) 
    {
        double[] result = new double[a.Length];
        for (int i = 0; i < a.Length; i++) result[i] = f(a[i]);
        return result;
    }
    static void Main() 
    {
        double[] a = {0.0, 0.5, 1.0};
        double[] squares = Apply(a, Square);
        double[] sines = Apply(a, Math.Sin);
        Multiplier m = new Multiplier(2.0);
        double[] doubles =  Apply(a, m.Multiply);
    }
}
           

Function

 委托類型執行個體可以引用需要使用 

double

 自變量并傳回 

double

 值的方法。 

Apply

 方法将給定的函數應用于 

double[]

 的元素,進而傳回包含結果的 

double[]

。 在 

Main

 方法中,

Apply

 用于向 

double[]

 應用三個不同的函數。

委托可以引用靜态方法(如上面示例中的 

Square

 或 

Math.Sin

)或執行個體方法(如上面示例中的 

m.Multiply

)。 引用執行個體方法的委托還會引用特定對象,通過委托調用執行個體方法時,該對象會變成調用中的 

this

還可以使用匿名函數建立委托,這些函數是便捷建立的“内聯方法”。 匿名函數可以檢視周圍方法的局部變量。 是以,可以更輕松地編寫上面的乘數示例,而無需使用 Multiplier 類:

double[] doubles =  Apply(a, (double x) => x * 2.0);
           

委托的一個有趣且有用的屬性是,它不知道也不關心所引用的方法的類;隻關心引用的方法是否具有與委托相同的參數和傳回類型。

特性

C# 程式中的類型、成員和其他實體支援使用修飾符來控制其行為的某些方面。 例如,方法的可通路性是由 

public

protected

internal

 和 

private

 修飾符控制。 C# 整合了這種能力,以便可以将使用者定義類型的聲明性資訊附加到程式實體,并在運作時檢索此類資訊。 程式通過定義和使用特性來指定此類額外的聲明性資訊。

以下示例聲明了 

HelpAttribute

 特性,可将其附加到程式實體,以提供指向關聯文檔的連結。

using System;

public class HelpAttribute: Attribute
{
    string url;
    string topic;
    public HelpAttribute(string url) 
    {
        this.url = url;
    }

    public string Url => url;

    public string Topic {
        get { return topic; }
        set { topic = value; }
    }
}
           

所有特性類都派生自标準庫提供的 Attribute 基類。 特性的應用方式為,在相關聲明前的方括号内指定特性的名稱以及任意自變量。 如果特性的名稱以 

Attribute

 結尾,那麼可以在引用特性時省略這部分名稱。 例如,可按如下方法使用 

HelpAttribute

[Help("https://docs.microsoft.com/dotnet/csharp/tour-of-csharp/attributes")]
public class Widget
{
    [Help("https://docs.microsoft.com/dotnet/csharp/tour-of-csharp/attributes", 
    Topic = "Display")]
    public void Display(string text) {}
}
           

此示例将 

HelpAttribute

 附加到 

Widget

 類。 還向此類中的 

Display

 方法附加了另一個 

HelpAttribute

。 特性類的公共構造函數控制了将特性附加到程式實體時必須提供的資訊。 可以通過引用特性類的公共讀寫屬性(如上面示例對 

Topic

 屬性的引用),提供其他資訊。

通過反射請求獲得特定特性時,将調用特性類的構造函數(由程式源提供資訊),并傳回生成的特性執行個體。如果是通過屬性提供其他資訊,那麼在特性執行個體傳回前,這些屬性會設定為給定值。

github位址: 

代碼彙總_1:

using System;
using Acme.Collections;
using System.Collections;
using System.Collections.Generic;

using System.IO;
namespace cs_07
{
    class Account {
        decimal balance = 100.00M;
        private readonly object syncObj = new object();
        public void withdraw(decimal amount){
            // keep lock status while operation 
            lock(syncObj){
                if(amount > balance){
                    throw new Exception("Insufficient funds");
                }
                balance -= amount;
                Console.WriteLine($"balance is {balance}");
            }
        }
    }
    class MainClass
    {
        public static void Main(string[] args)
        {
            if(0 == 1){
                Console.WriteLine("Hello Beyond!");
                Acme.Collections.Stack s = new Acme.Collections.Stack();
                s.push(5);
                s.push(6);
                s.push(7);
                Console.WriteLine(s.Pop());
                Console.WriteLine(s.Pop());
                Console.WriteLine(s.Pop());
                Console.WriteLine(s.Pop());
            }
            if(1 == 2){
                int i = 5267;
                object o = i;
                int j = (int)o;
                    
            }

            if (2 == 3)
            {
                int a;
                int b = 6, c = 7;
                a = 5;
                Console.WriteLine(a + b + c);

            }

            if (3 == 4)
            {
                const float pi = 3.1415927f;
                const int r = 5;
                Console.WriteLine(pi * r * r);

            }

            if (4 == 5)
            {
                int i;
                i = 5267;
                Console.WriteLine(i);
                i++;
                Console.WriteLine(i);
            }


            if (5 == 6)
            {
                if(args.Length == 0){
                    Console.WriteLine("No Arguments");
                }else{
                    Console.WriteLine("Has Arguments");
                }
            }

            if (6 == 7)
            {
                switch(args.Length){
                    case 0:
                        Console.WriteLine("No Arguments");
                        break;
                    case 1:
                        Console.WriteLine("1 Arguments");
                        break;
                    default:
                        // attention please!
                        Console.WriteLine($"{args.Length} Arguments");
                        break;
                }
            }

            if (7 == 8)
            {
                int i = 0;
                while(i < args.Length){
                    Console.WriteLine(args[i]);
                    i++;
                }
            }

            if (8 == 9)
            {
                string s;
                do
                {
                    s = Console.ReadLine();
                    Console.WriteLine(s);
                } while (!string.IsNullOrEmpty(s));
                // 不是空的, 就能一直輸入 
            }

            if (9 == 10)
            {
                for (int i = 0; i < args.Length; i++){
                    Console.WriteLine(args[i]);
                }
            }

            if (10 == 11)
            {
                foreach(string s in args){
                    Console.WriteLine(s);
                }
            }

            if (11 == 12)
            {
                while(true){
                    string s = Console.ReadLine();
                    if(string.IsNullOrEmpty(s)){
                        break;
                    }
                    Console.WriteLine(s);
                }
            }

            if (12 == 13)
            {
                for (int i = 0; i < args.Length; i++){
                    if(args[i].StartsWith("/")){
                        continue;
                    }
                    Console.WriteLine(args[i]);
                }
            }

            if (13 == 14)
            {
                int i = 0;
                goto checkStatement;

            loopStatement:
                    Console.WriteLine(args[i++]);
            checkStatement:
                    if(i < args.Length){
                        goto loopStatement;
                    }
            }

            if (14 == 15)
            {
                Console.WriteLine(add(6, 7));
                return;
            }

            if (15 == 16)
            {
                foreach(int i in Range(-5,5)){
                    Console.WriteLine(i);
                }
            }

            if (16 == 17)
            {
                foreach(int yieldReturnValue in Power(2,8)){
                    Console.Write("{0} ", yieldReturnValue);
                }
            }

            if (17 == 18)
            {
                try{
                    if(args.Length != 2){
                        throw new InvalidOperationException("Two numbers required");
                    }
                    // string --> double 
                    double x = double.Parse(args[0]);
                    double y = double.Parse(args[1]);
                    Console.WriteLine(divide(x, y));
                    Console.ReadKey();
                }catch (InvalidOperationException e){
                    Console.WriteLine(e.Message);
                    Console.ReadKey();
                }catch(DivideByZeroException e){
                    Console.WriteLine(e.Message);
                    Console.ReadKey();
                }finally{
                    Console.WriteLine("Bye Beyond!");
                    Console.ReadKey();
                }
            }

            if (18 == 19)
            {
                int x = int.MaxValue;
                // Console.WriteLine($"0Max Int: {x}");

                // 如果使用了unchecked則不會檢查溢出,算錯了也不會報錯。
                unchecked{
                    Console.WriteLine($"1Max Int: {x + 1}");
                }

                // 如果使用了checked發生數學運算溢出時會抛出OverflowException;
                checked
                {
                    Console.WriteLine($"2Max Int: {x + 1}");
                }
            }

            if (19 == 20)
            {
                Account account = new Account();
                account.withdraw(40);
                account.withdraw(40);
                account.withdraw(40);
            }

            if (20 == 20)
            {
                // file exists in bin/Debug subFolder
                using (TextWriter w = File.CreateText("test.txt")){
                    w.WriteLine("Line One");
                    w.WriteLine("Line Two");
                    w.WriteLine("Line Three");
                }
            }


        }

        // asdf ===================== function definition=========
        static int add(int a, int b){
            return a + b;
        }

        static IEnumerable<int> Range(int from ,int to ){
            for (int i = from; i < to; i++){
                yield return i;
            }
            yield break;
        }

        static System.Collections.Generic.IEnumerable<int>
                            Power(int number , int exponent){
            int yieldReturnValue = 1;
            for (int i = 0; i < exponent; i++){
                yieldReturnValue = yieldReturnValue * number;
                yield return yieldReturnValue;
            }
        }

        static double divide(double x , double y){
            if(y == 0){
                throw new DivideByZeroException();
            }
            return x / y; 
        }


    }
}
           

代碼彙總_2:

using System;
using System.Collections.Generic;

namespace cs_08
{
    class MainClass
    {
        public static void Main(string[] args)
        {
            if(0 == 1){
                Point p1 = new Point(6, 7);
                Point p2 = new Point(5, 20);
            }
            if (1 == 2)
            {
                Pair<int, string> p1 = new Pair<int, string>
                {
                    first = 1,
                    second = "Beyond"
                };
                int i = p1.first;
                string str = p1.second;
                Console.WriteLine($"{i}  {str}");
                Console.ReadKey();
            }
            if(2 == 3){
                Point3D p3 = new Point3D(5, 6, 7);
                Console.WriteLine(p3.z);
            }
            if (3 == 4)
            {
                Color c1 = new Color(66, 66, 66);
            }
            if(4 == 5){
                // attention please
                int i = 6, j = 7;
                swap(ref i, ref j);
                Console.WriteLine($"i : {i}, j: {j}");
            }
            if (5 == 6)
            {
                divide(10, 3, out int result, out int remainder);
                Console.WriteLine("{0} {1}", result, remainder);
                Console.WriteLine($"{result} {remainder}");
            }

            if (6 == 7)
            {
                int[] a = new int[3];
                a[0] = 5;
                a[1] = 6;
                a[2] = 7;
                writeLine("hello beyond", a[0],a[1],a[2]);
            }

            if (7 == 8)
            {
                int i = 0;
                int j;
                while(i < 10){
                    j = i * i;
                    Console.WriteLine($"{i} * {i} = {j}");
                    i += 1;
                }
            }

            if(8 == 9){
                Entity e1 = new Entity();
                Console.WriteLine($"{e1.GetSerialNo()} , {Entity.GetNextSerailNo()}");

                Entity e2 = new Entity();
                Console.WriteLine($"{e2.GetSerialNo()} , {Entity.GetNextSerailNo()}");

            }
            if(9 == 10){
                VariableReference left = new VariableReference("x");
                Constant right = new Constant(67);
                // polymorphic
                Expression baseEx = new Operation(left,
                                             '+',
                                             right);
                Dictionary<string, object> dict = new Dictionary<string, object>();
                dict["x"] = 52;
                Console.WriteLine(baseEx.Evaluate(dict));

            }

            if (10 == 11)
            {
                // show how to calculate        x * (y + 5)
                // attention please: parenthesis first
                VariableReference left1 = new VariableReference("y");
                char op1 = '+';
                Constant right1 = new Constant(5);
                // polymorphic
                Expression baseEx1 = new Operation(left1,
                                                   op1,
                                                   right1);


                VariableReference left2 = new VariableReference("x");
                char op2 = '*';
                Expression baseEx2 = new Operation(left2, op2, baseEx1);


                Dictionary<string, object> dict = new Dictionary<string, object>();
                // x * (y + 5)
                dict["x"] = 6;
                dict["y"] = 7;
                Console.WriteLine(baseEx2.Evaluate(dict));  

                // x * (y + 5)
                dict["x"] = 2;
                dict["y"] = 4;
                Console.WriteLine(baseEx2.Evaluate(dict));  

            }

            if(11 == 12){
                Func();
                Func(1);
                Func(1.0);
                Func("1");
                Func((double)1);
                Func((object)1);
                Func<int>(1);
                Func(1,1);
            }

            if (12 == 13)
            {
                // 索引器、事件、運算符和終結器
                List<string> list = new List<string>(1);
                list.Add("keke");
                list.Add("menma");
                list.Add("mathildar");
                Console.WriteLine(list.Capacity);

                Console.WriteLine(list[0]);
                Console.WriteLine(list[1]);
                Console.WriteLine(list[0] == list[1]);
                Console.WriteLine(list[0].Equals(list[1]));

            }

            if (13 == 14)
            {
                // 索引器、事件、運算符和終結器
                BeyondEventHandler eventHandler = new BeyondEventHandler(Boya.MeetAGirlFunction);
                // 
                eventHandler += Boya.FallInLoveFunction;

                // 觸發事件
                eventHandler();
            }

            if(14 == 15){
                List<string> names = new List<string>();
                names.Capacity = 100;   // Invokes set accessor
                int i = names.LastPosition;    // Invokes get accessor
                int j = names.Capacity; // Invokes get accessor
                Console.WriteLine($"index: {i}, capacity: {j}");
            }

            if (15 == 16)
            {
                List<string> names = new List<string>();
                names.Add("Mathildar");
                names.Add("Beyond");
                names.Add("Lolita");

                for (int i = 0; i < names.LastPosition; i++){
                    string s = names[i];
                    names[i] = s.ToUpper();
                    Console.WriteLine(names[i]);
                }
            }

            if (16 == 17){
                // 使用 += 和 -= 運算符分别可以附加和删除事件處理程式
                List<string> names = new List<string>();
                // 訂閱感興趣的事件
                names.isChanged += new EventHandler(ListHasChangedListener);

                names.Add("Beyond");
                names.Add("Mathilder");
                names.Add("Lolita");

                Console.WriteLine(listChangeCount); 
            }

            if (17 == 18)
            {
                List<int> a = new List<int>();
                a.Add(6);
                a.Add(7);

                List<int> b = new List<int>();
                b.Add(6);
                b.Add(7);

                Console.WriteLine(a == b);

                b.Add(520);
                Console.WriteLine(a == b);
            }

            if(18 == 19){
                // create 101 object
                Point[] pointArr = new Point[100];
                for (int i = 0; i < 100; i++){
                    pointArr[i] = new Point(i, i);
                }
            }

            if (19 == 20)
            {
                // create 1 array object
                PointStruct[] pointArr = new PointStruct[100];
                for (int i = 0; i < 100; i++)
                {
                    pointArr[i] = new PointStruct(i, i);
                }
                Console.WriteLine(pointArr.Length);
            }

            if(20 == 21){
                int[] a = new int[10];
                for (int i = 0; i < a.Length; i++){
                    a[i] = i * i;
                }
                for (int i = 0; i < a.Length; i++){
                    Console.WriteLine($"a[{i}] = {a[i]}");
                }
            }

            if(21 == 22){
                int[] a1 = new int[10];
                int[,] a2 = new int[10, 5];
                int[,,] a3 = new int[10, 5, 2];
            }

            if (22 == 23)
            {
                // 長度不必全都一樣
                int[][] a = new int[3][];
                a[0] = new int[10];
                a[1] = new int[5];
                a[2] = new int[20];
                for (int i = 0; i < a.Length; i++)
                {
                    Console.WriteLine($"a[{i}] = {a[i]}");
                }
            }

            if (23 == 24)
            {
                int[] a = new int[]{5,6,7};
                for (int i = 0; i < a.Length; i++)
                {
                    Console.WriteLine($"a[{i}] = {a[i]}");
                }
            }

            if (24 == 25)
            {
                int[] a = { 5, 6, 7 };
                for (int i = 0; i < a.Length; i++)
                {
                    Console.WriteLine($"a[{i}] = {a[i]}");
                }
            }

            if (25 == 26)
            {
                int[] tempArr = new int[3];
                tempArr[0] = 5;
                tempArr[1] = 6;
                tempArr[2] = 7;
                int[] a = tempArr;

                for (int i = 0; i < a.Length; i++)
                {
                    Console.WriteLine($"a[{i}] = {a[i]}");
                }
            }

            if (26 == 27)
            {
                EditBox editBox = new EditBox();
                IControl ctrl = editBox;
                IDataBound dbBount = editBox;

                Console.WriteLine(editBox);
                Console.WriteLine(ctrl);
                Console.WriteLine(dbBount);
            }

            if (27 == 28)
            {
                object obj = new EditBox();
                IControl ctrl = (IControl)obj;
                IDataBound dbBount = (IDataBound)obj;

                Console.WriteLine(obj);
                Console.WriteLine(ctrl);
                Console.WriteLine(dbBount);
            }

            if (28 == 29)
            {
                EditBox2 editBox = new EditBox2();
                // editBox.Paint();
                IControl ctrl = editBox;
                ctrl.Paint();
            }

            if (29 == 30)
            {
                ColorRGB c = ColorRGB.Red;
                PrintColor(c);
                PrintColor(ColorRGB.Blue);
            }

            if (30 == 31)
            {
                int i = (int)ColorRGB.Blue;
                ColorRGB c = (ColorRGB)2;
                Console.WriteLine(i); // 2
                Console.WriteLine(c); // Blue
            }

            if(31 == 32){
                // 0 特殊, 不用顯示轉換
                ColorRGB c = 0;
                Console.WriteLine(c); // Red
            }

            if (32 == 33)
            {
                double[] arr = { 0.0, 0.5, 1.0 };
                double[] squareArr = Apply(arr, Square);
                for (int i = 0; i < squareArr.Length; i++)
                {
                    Console.WriteLine($"squareArr[{i}] = {squareArr[i]}");
                }

                double[] sinArr = Apply(arr, Math.Sin);
                for (int i = 0; i < sinArr.Length; i++)
                {
                    Console.WriteLine($"sinArr[{i}] = {sinArr[i]}");
                }

                Multiplier multi = new Multiplier(2.0);
                double[] multiArr = Apply(arr, multi.multiplyFunc);
                for (int i = 0; i < multiArr.Length; i++)
                {
                    Console.WriteLine($"multiArr[{i}] = {multiArr[i]}");
                }
            }

            if (33 == 34)
            {
                double[] arr = { 0.0, 0.5, 1.0 };
                // 使用匿名函數建立委托
                double[] squareArr = Apply(arr, (double x) => x * 2.0);
                for (int i = 0; i < squareArr.Length; i++)
                {
                    Console.WriteLine($"squareArr[{i}] = {squareArr[i]}");
                }
            }

            if (34 == 34)
            {
                
            }

        }

        // ==========other functions============
        static void swap(ref int x, ref int y)
        {
            int temp = x;
            x = y;
            y = temp;
        }

        static void divide(int x, int y, out int result , out int remainder){
            result = x / y;
            remainder = x % y;
        }

        static void writeLine(string str, params object[] args){
            Console.WriteLine($"Length is : {args.Length}, the last number is : {args[2]}");
        }

        static void Func(){
            Console.WriteLine("F()");
        }
        static void Func(object o)
        {
            Console.WriteLine("F(object)");
        }

        static void Func(int i)
        {
            Console.WriteLine("F(int)");
        }

        static void Func(double d)
        {
            Console.WriteLine("F(double)");
        }

        static void Func<T>(T t)
        {
            Console.WriteLine("F<T>(T)");
        }

        static void Func(double d1, double d2)
        {
            Console.WriteLine("F(double,double)");
        }

        // ========================

        public delegate void BeyondEventHandler();

        // ========================

        static int listChangeCount;
        static void ListHasChangedListener(object sender, EventArgs e){
            listChangeCount++;
            Console.WriteLine($"監聽: {listChangeCount}");
        }

        // ========================

        static void PrintColor(ColorRGB color){
            switch(color){
                case ColorRGB.Red:
                    Console.WriteLine("this is Red Color");
                    break;
                case ColorRGB.Green:
                    Console.WriteLine("this is Green Color");
                    break;
                case ColorRGB.Blue:
                    Console.WriteLine("this is Blue Color");
                    break;
                default:
                    Console.WriteLine("this is Unkown Color");
                    break;
            }
        }

        // ========================

        static double Square(double x){
            return x * x;
        }
        static double[] Apply(double[] arr, Function func){
            double[] result = new double[arr.Length];
            for (int i = 0; i < arr.Length; i++){
                result[i] = func(arr[i]);
            }
            return result;
        }

    }

    // ===========other classes===========

    class Point {
        public int x, y;
        public Point(int x, int y){
            this.x = x;
            this.y = y;
        }
    }

    class Pair<TFirst, TSecond>{
        public TFirst first;
        public TSecond second;
    }

    class Point3D: Point {
        public int z;
        public Point3D(int x,int y,int z ) : base(x,y)
        {
            this.z = z;
        }
    }
    // ===============================
    class Color{
        public static readonly Color Black = new Color(0, 0, 0);
        public static readonly Color White = new Color(255, 255, 255);
        public static readonly Color Red = new Color(255, 0, 0);
        public static readonly Color Green = new Color(0, 255, 0);
        public static readonly Color Blue = new Color(0, 0, 255);
        private byte r, g, b;
        public Color(byte r, byte g, byte b){
            this.r = r;
            this.g = g;
            this.b = b;
        }
    }
    // ===============================
    class Entity{
        static int nextSerialNo;
        int serailNo;
        public Entity(){
            this.serailNo = nextSerialNo++;
        }            
        public int GetSerialNo(){
            return this.serailNo;
        }
        public static int GetNextSerailNo(){
            return nextSerialNo;
        }
        public static void SetNextSerialNo(int val){
            Entity.nextSerialNo = val;
        }

    }
    // ===============================
    abstract class Expression{
        public abstract double Evaluate(Dictionary<string, object> dict);
    }
    class Constant: Expression{
        double value;
        public Constant(double value){
            this.value = value;
        }
        public override double Evaluate(Dictionary<string, object> dict)
        {
            return this.value;
        }
    }
    class VariableReference: Expression {
        string name;
        public VariableReference(string name){
            this.name = name;
        }
        public override double Evaluate(Dictionary<string, object> dict)
        {
            object obj = dict[name];
            if(obj == null){
                throw new Exception("UnKnow Variabel: " + name);
            }
            return Convert.ToDouble(obj);
        }
    }
    class Operation: Expression{
        Expression left;
        char op;
        Expression right;
        public Operation(Expression left, char op , Expression right){
            this.left = left;
            this.op = op;
            this.right = right;
        }
        public override double Evaluate(Dictionary<string, object> dict)
        {
            double x = left.Evaluate(dict);
            double y = right.Evaluate(dict);
            switch(op){
                case '+': return x + y;
                case '-': return x - y;
                case '*': return x * y;
                case '/': return x / y;    
            }
            throw new Exception("UnKnow operator");
        }
    }
    // ===============================
    class List<T>
    {
        // const int defaultCapacity = 4;

        // Fields
        T[] items;
        int lastPosition;   

        public List(int capacity = 2)
        {
            items = new T[capacity];
        }

        // Properties
        public int LastPosition => lastPosition;
        // what ?
        public int Capacity
        {
            get
            {
                return items.Length;
            }
            // value なに?
            set
            {
                if (value < this.lastPosition)
                {
                    value = this.lastPosition;
                }
                // copy to large new array
                if (value != items.Length)
                {
                    T[] newItems = new T[value];
                    Array.Copy(items, 0, newItems, 0, this.lastPosition);
                    items = newItems;
                }
            }
        }

        // what ??
        public T this[int lastPosition]
        {
            get
            {
                return this.items[lastPosition];
            }
            set
            {
                this.items[lastPosition] = value;
                OnChangedHandler();
            }
        }

        // methods
        public void Add(T item)
        {
            if (lastPosition == Capacity)
            {
                Capacity = lastPosition * 2;
            }
            items[lastPosition] = item;
            lastPosition++;
            OnChangedHandler();
        }

        // event
        public event EventHandler isChanged;

        // Operators
        public static bool operator ==(List<T> a, List<T> b) =>
            Equals(a, b);
        public static bool operator !=(List<T> a, List<T> b) =>
            !Equals(a, b);


        // what ???
        protected virtual void OnChangedHandler() =>
             isChanged?.Invoke(this, EventArgs.Empty);
    

        public override bool Equals(Object other) =>
            Equals(this, other as List<T>);

        // what ???
        static bool Equals(List<T>a, List<T>b){
            if(Object.ReferenceEquals(a,null)){
                return Object.ReferenceEquals(b, null);
            }

            // a is not null
            if(Object.ReferenceEquals(b,null) || a.lastPosition != b.lastPosition){
                return false;
            }

            // a,b is not null  
            // a.count == b.count
            for (int i = 0; i < a.lastPosition; i++){
                if(!object.Equals(a.items[i],b.items[i])){
                    return false;
                }
            }
            return true;
        }

    }

    // ===============================
    class Boya
    {
        public static void MeetAGirlFunction()
        {
            Console.WriteLine("boya meet a girl");
        }
        public static void FallInLoveFunction()
        {
            Console.WriteLine("boya fall in love");
        }
    }


    // ========= other struct =======================
    struct PointStruct {
        public int x, y;
        public PointStruct(int x, int y){
            this.x = x;
            this.y = y;
        }
    }


    // ========= other interface =======================
    interface IControl {
        void Paint();
    }
    interface ITextBox: IControl{
        void SetText(string text);
    }
    interface IListBox: IControl{
        void SetItems(string[] items);
    }
    interface IComboBox: ITextBox, IListBox{}
    interface IDataBound{
        void Bind(Binder b);
    }
    class EditBox: IControl, IDataBound{
        public void Paint(){
            
        }
        public void Bind(Binder b){
            
        }
    }

    class EditBox2: IControl, IDataBound{
        // 顯式接口成員實作代碼 and 不會将成員設為公共成員
        void IControl.Paint(){
            Console.WriteLine("EditBox2 Paint Function");
        }
        void IDataBound.Bind(Binder b){}
    }

    // 手動打更新檔 寫一個Binder類
    class Binder{
        
    }

    // ========= other enum =======================
    enum ColorRGB{
        Red,
        Green,
        Blue
    }

    enum Alignment : sbyte
    {
        Left = -1,
        Center = 0,
        Right = 1
    }

    // ========= other class =======================
    class Multiplier{
        double factor;
        public Multiplier(double factor){
            this.factor = factor;
        }
        public double multiplyFunc(double x){
            return x * factor;
        }
    }

    delegate double Function(double x);

    // ========= other Attribute =======================
    class BeyondAttribute: Attribute{
        string url;
        string topic;
        public BeyondAttribute(string url){
            this.url = url;
        }
        public string Url => url;
        public string Topic{
            get{
                return topic;
            }
            set{
                topic = value;
            }
        }
    }

    [Beyond("https://docs.microsoft.com/dotnet/csharp/tour-of-csharp/attributes")]
    class Widget{
        [Beyond("https://docs.microsoft.com/dotnet/csharp/tour-of-csharp/attributes",
                Topic ="Display")]
        public void Display(string text){
            
        }
    }

}
           

未完待續,下一章節,つづく