2015 年 12 月

第 30 卷,第 13 期

本文章是由機器翻譯。

Essential .NET - 設計 C# 7

Mark Michaelis | 2015 年 12 月

Mark Michaelis閱讀本文時,C# 7 設計團隊將有討論過,規劃、 試驗和約一年的程式設計。在這一期,我將範例中的一些概念都在探索。

在檢閱,留意,此時這些仍然是 C# 7 中要包含哪些的想法。觀念小組有只談到,而其他人所做它而言實驗性的實作。無論如何,無的概念是設定為無效。許多可能永遠不會看到一天; 光線即使是往後可能裁切語言最終處理的最後階段。

宣告不可為 Null 及非可為 Null 參考型別

C# 7 討論中的最主要觀念也許是在使用 null 進一步改進事項,做為 C# 6.0 的 null 條件運算子同樣的道理。其中一個最簡單的改良功能可能是編譯器或分析器驗證之存取的可為 null 的型別執行個體就會做為型別不是,檢查開頭事實上,null。

在當 null 並不恰當的參考型別上的情況下,如果您無法避免 null 完全嗎? 觀念上就是宣告參考型別會允許 null 值 (字串?),或避免 null (字串!) 的目的。理論上,甚至可能假設新的程式碼中的所有參考型別宣告,根據預設,不可以是 null。不過,隨著所我 「 基本 C# 6.0"書籍共同作者,Eric Lippert 確保參考型別不會 null 在編譯時期是極為困難 (bit.ly/1Rd5ekS)。即便如此,或許可以是識別案例,其中型別無法可能是 null,而且尚未解除參考而不會檢查它不。或者,為 null 的型別可能會指派的 null,儘管不是宣告意圖的案例。

更多的權益,小組所討論的科技運用參數不可為 null 的型別宣告,使 (雖然這可能就需要選擇加入決定来避免使用預期的效能負面影響,除非便可在編譯時期),它會自動產生 null 檢查的可能性。

(C# 2.0 諷刺的是,加入可為 null 的實值型別,因為有許多情況下 — 例如,從資料庫擷取資料,則必須讓包含 null 的整數。因此現在,在 C# 7 中,小組會想要支援參考類型相反。)

一個有趣的考量因素與參考型別支援不可為 null 的項目 (例如,字串! 文字) 是實作會在通用的中繼語言 (CIL)。提議的兩個最受歡迎是是否要將它對應至 < T > 不允許為 Null 的型別語法,或是利用 [Nullable] 字串內容的屬性。後者是目前的慣用的方法。

Tuple

Tuple 是 C# 7 考量的另一項功能。這是語言的主題都出現在多個情況下,針對較舊版本,但仍未相當進入生產環境。其概念是,就有可能,宣告可以包含多個值,而同樣地,方法可以傳回多個值的宣告集合中的型別。請考慮下列範例程式碼,以了解概念:

public class Person
{
  public readonly (string firstName, int lastName) Names; // a tuple
  public Person((string FirstName, string LastName)) names, int Age)
  {
    Names = names;
  }
}

如清單所示,tuple 的支援,您可以宣告為 tuple 的型別,有兩個 (含) 以上的值。這可以利用隨處可用的資料類型,包括做為欄位、 參數、 變數宣告或甚至方法會傳回。例如,下列程式碼片段會從方法傳回一個 tuple:

public (string FirstName, string LastName) GetNames(string! fullName)
{
  string[] names = fullName.Split(" ", 2);
  return (names[0], names[1]);
}
public void Main()
{
  // ...
  (string first, string last) = GetNames("Inigo Montoya");
  // ...
}

這份清單中沒有傳回一個 tuple 和變數宣告中的第一個和最後一個 GetNames 結果會指派至的方法。請注意,指派為基礎之 tuple (不接收變數的名稱) 中的順序。考慮一些我們必須立即使用的替代方法,在陣列或集合,out 參數的自訂型別 — tuple 是一個不錯的選擇。

有許多選項,可能會伴隨 tuple。以下是一些考量:

  • Tuple 可能具有名為或未命名的屬性,如下:
var name = ("Inigo", "Montoya")

和:

var name = (first: "John", last: "Doe")
  • 結果可能是匿名型別或明確的變數,例如:
var name = (first: "John", last: "Doe")

或:

(string first, string last) = GetNames("Inigo Montoya")
  • 您可能無法做為中轉換至 tuple,陣列:
var names = new[]{ "Inigo", "Montoya" }
  • 您可以存取個別的 tuple 項目名稱,做為:
Console.WriteLine($”My name is { names.first } { names.last }.”);
  • 可能是資料型別推斷,在未識別出明確 (遵循相同的方式,通常使用匿名型別)

雖然有 tuple 的複雜度,大多數的情況下它們遵循結構已經確立內語言,因此他們有很強式支援包含在 C# 7。

模式的比對

模式比對,也是常見的主題,在 C# 7 設計小組討論。可能的更容易了解的呈現的其中一個會擴充的參數 (和 if) 支援 case 陳述式,而非只是常數運算式模式的陳述式。(對應到展開的 case 陳述式,參數的運算式型別不會受限於具有其中一個對應的常數值的型別)。搭配模式比對,您可以查詢參數運算式的模式,例如 switch 運算式是否為特定型別、 型別與特定成員或甚至比對特定 「 模式 」 或運算式的型別。例如,請考慮如何 obj 可能大於 2 x 值點類型為:

object obj;
// ...
switch(obj) {
  case 42:
    // ...
  case Color.Red:
    // ...
  case string s:
    // ...
  case Point(int x, 42) where (Y > 42):
    // ...
  case Point(490, 42): // fine
    // ...
  default:
    // ...
}

有趣的是,指定運算式做為 case 陳述式,它也可能需要允許做為引數上 goto case 陳述式的運算式。

若要支援的類型點,情況必須為某種類型的處理模式比對的點上的成員。在此情況下,需要的是成員採用兩個引數是 int 類型。成員,例如,例如:

public static bool operator is (Point self out int x, out int y) {...}

請注意,不含 where 運算式,大小寫的點 (490,42) 可能永遠不會達到,造成編譯器將發出錯誤或警告。

Switch 陳述式的限制因素是它不會傳回值,但卻而執行的程式碼區塊。模式比對的新增的功能可能會傳回值,例如 switchexpression 的支援:

string text = match (e) { pattern => expression; ... ; default => expression }

同樣地,運算子可以支援模式比對,可讓不只一個型別檢查,但可能更泛型類型上的特定成員是否存在有關查詢的支援。

記錄

在縮寫"建構函式 」 的宣告語法 (但最後被拒絕) 視為在 C# 6.0 的延續,沒有支援內嵌建構函式宣告在類別定義中,概念為 「 記錄 」。 例如,請考慮下列宣告:

class Person(string Name, int Age);

這個簡單的陳述式會自動產生下列:

  • 建構函式:
public Person(string Name, int Age)
{
  this.Name = Name;
  this.Age = Age;
}
  • 唯讀屬性,以便建立不可變的類型
  • 等號比較實作 (例如 GetHashCode,等於] 運算子 = = 運算子! = 等等)
  • 預設的實作 ToString
  • 模式比對 」 運算子的支援

雖然會產生大量的程式碼 (考慮建立它所有的程式碼只有一個簡短的列),希望是也可以手動撰寫程式碼為何基本上未定案實作相對大量的捷徑。此外,所有的程式碼可以視為 「 預設 」,明確實作它的任何會優先使用並防止產生相同的成員。

更麻煩的問題與記錄相關聯的其中一個是序列化的處理方式。大概運用記錄還不是清除的項目,如果任何項目,也可以透過支援這類記錄的序列化和資料傳輸物件 (Dto) 時很常見。

在關聯中的記錄是使用運算式的支援。使用運算式讓現有的物件為基礎的新物件具現化。指定 person 物件宣告中,比方說,您可以建立的新執行個體,透過下列運算式:

Person inigo = new Person("Inigo Montoya", 42);
Person humperdink = inigo with { Name = "Prince Humperdink" };

產生的程式碼對應的運算式看起來應該像:

Person humperdink = new Person(Name: "Prince Humperdink", Age: inigo.42 );

替代的建議,不過,是的而不是根據簽章的建構函式的運算式,可能會比將它轉譯為在 With 方法的引動過程:

Person humperdink = inigo.With(Name: "Prince Humperdink", Age: inigo.42);

非同步資料流

若要增強的 C# 7 中的非同步支援,處理非同步順序的概念很有趣。比方說,指定 IAsyncEnumerable,與目前的屬性和工作 < bool > MoveNextAsync 方法,您可以反覆使用 foreach 的 IAsyncEnumerable 執行個體並讓編譯器小心以非同步方式叫用資料流中的每個成員的 — 執行 await,若要找出是否有另一個項目 (可能是頻道) 的順序來處理程序。有的一些注意事項此評估,其中最重要的是所有的 LINQ 標準查詢運算子傳回 IAsyncEnumerable 可能發生的潛在 LINQ 膨脹。另外,它不是特定如何公開 CancellationToken 支援,甚至是 Task.ConfigureAwait。

命令列上的 C#

Windows PowerShell 如何讓 Microsoft.NET Framework 中的命令列介面 (CLI) 提供熱愛,我要特別像樣的 (可能是我最愛的功能列入考量) 的其中一個區域是支援使用 C# 命令列。它是以一般方式稱為讀取、 評估、 列印、 Loop (REPL) 支援的概念更多。為其中一個是希望,REPL 支援會伴隨著 C# 指令碼不需要 (例如類別宣告) 的所有一般程序中不需要這類典禮的一般案例。而不需要編譯步驟中,複寫需要新的指示詞參考組件和 NuGet 套件,以及匯入其他檔案。討論提案目前可支援:

  • #r 參考其他組件或 NuGet 封裝。一種變化是 #r!,這甚至會允許存取內部成員,但是有一些限制。(這被為了情況下您要在其中存取會對原始碼的組件。)
  • #l 包含整個目錄 (類似於 F #)。
  • 若要匯入其他 C# 指令碼檔案,如同您會將它加入您的專案現在順序很重要,但 #load。(請注意,匯入一個.cs 檔案可能不支援因為 C# 指令碼中不允許的命名空間)。
  • 若要在執行時開啟效能診斷 #time。

您可以預期 C# REPL 釋出與 Visual Studio 2015 Update 1 (以及支援相同的功能集更新互動式視窗) 的第一個的版本。如需詳細資訊請參閱 Itl.tc/CSREPL, ,以及下個月的專欄。

總結

一整年的材料,會有太多能探索所有的設計團隊已執行,還有我觸及的觀念,即使有更多詳細資料 (需要注意的事項和優點),需要考量。希望,不過,您現在已了解小組探索和他們期望如何改善已出色的 C# 語言。如果您想要直接檢視 C# 7 設計注意事項,並可能提供您自己的意見反應,可以跳到應在討論 bit.ly/CSharp7DesignNotes


Mark Michaelis是的 IntelliTect,他擔任其技術架構設計人員和培訓講師的創辦人。近二十他 Microsoft MVP 和 Microsoft 區域經理自已 2007年。Michaelis 服務於多個 Microsoft 軟體設計檢閱小組,包括 C#、 Microsoft Azure、 SharePoint 和 Visual Studio ALM。他開發人員會議上發表演說,並且已撰寫許多本書,包括他最新,「 基本 C# 6.0 (第 5 版) 」 (itl.tc/EssentialCSharp)。在 Facebook 上連絡他 facebook.com/Mark.Michaelis, 他的部落格上 IntelliTect.com/Mark, twitter: @markmichaelis 或透過電子郵件地 mark@IntelliTect.com

感謝以下的微軟技術專家對本文的審閱: Torgerson mads