本文章是由機器翻譯。

C#

如何 C# 6.0 簡化、 澄清和凝聚你的代碼

Mark Michaelis

C# 6.0 不是 C# 程式設計中的激進革命。不像引入泛型的 C# 2.0、 C# 3.0 和其開創性的方式到程式集合與 LlNQ 或簡化的非同步程式設計模式 5.0 C# 中,C# 6.0 不會改變發展。這就是說,C# 6.0 將改變在特定的場景,特點是效率高太多,你可能會忘了還有另一種方式,他們的代碼編寫 C# 代碼的方式。它介紹了新的語法快捷方式裝瘋賣傻,減少儀式和最終使編寫 C# 代碼的精簡。在這篇文章,我要去鑽研新的 C# 6.0 功能集的詳細資訊,使這一切成為可能。具體來說,我將重點介紹在構思圖中所示中所列的專案圖 1

C# 6.0 思維導圖
圖 1 C# 6.0 思維導圖

注意到許多此處的例子來自于我的書下, 一版"基本的 C# 6.0" (艾迪生-Wesley 專業)。

使用靜態

許多 C# 6.0 功能可以利用最基本的主控台程式。例如,使用現在支援在特定類中一種稱為的使用功能上呈現在全球範圍內,沒有任何一種類型首碼的靜態方法,如中所示的靜態指令圖 2。因為 System.Console 是一個靜態類,如何可以利用此功能的一個很明顯的例子。

圖 2 使用靜態指令減少了代碼內的噪音

using System;
using static System.ConsoleColor;
using static System.IO.Directory;
using static System.Threading.Interlocked;
using static System.Threading.Tasks.Parallel;
public static class Program
{
  // ...
  public static void Main(string[] args)
  {
    // Parameter checking eliminated for elucidation.
    EncryptFiles(args[0], args[1]);
  }
  public static int EncryptFiles(
    string directoryPath, string searchPattern = "*")
  {
    ConsoleColor color = ForegroundColor;
    int fileCount = 0;
    try
    {
      ForegroundColor = Yellow
      WriteLine("Start encryption...");
      string[] files = GetFiles(directoryPath, searchPattern,
        System.IO.SearchOption.AllDirectories);
      ForegroundColor = White
      ForEach(files, (filename) =>
      {
        Encrypt(filename);
        WriteLine("\t'{0}' encrypted", filename);
        Increment(ref fileCount);
      });
      ForegroundColor = Yellow
      WriteLine("Encryption completed");
    }
    finally
    {
      ForegroundColor = color;
    }
    return fileCount;
  }
}

此示例中,正在使用的 System.ConsoleColor、 System.IO.Directory、 System.Threading.Interlocked 和 System.Threading.Tasks.Parallel 靜態指令。 這些直接啟用眾多方法、 屬性和枚舉的調用:前景色,應使用 WriteLine、 GetFiles、 增量、 黃色、 白色和 ForEach。在每個情況下,這消除了需要加以限定與它的類型的靜態成員。(對於那些你使用Visual Studio2015年預覽或早些時候,該語法不包括將"靜態"關鍵字添加使用後,所以它只"System.Console,"例如使用。此外,不直到後Visual Studio2015年預覽並使用枚舉和結構除了靜態的類的靜態指令工作。)

大多數情況下,消除類型限定詞不顯著降低清晰的代碼,即使有更少的代碼。在主控台程式中的應使用 WriteLine 是相當明顯的也是對 GetFiles 的調用。而且,因為使用加法 System.Threading.Tasks.Parallel 靜態指令是明顯的有意,ForEach 是定義平行的 foreach 迴圈,每個版本的 C# 中,看起來 (如果你眯著眼睛正好),更多和更像是 C# foreach 語句的一種方式。

明顯注意到使用靜態指令是照顧清晰不犧牲了。例如,請考慮在定義的加密函數圖 3

圖 3 含糊不清的存在的調用 (與運營商的名稱)

private static void Encrypt(string filename)
  {
    if (!Exists(filename)) // LOGIC ERROR: Using Directory rather than File
    {
      throw new ArgumentException("The file does not exist.", 
        nameof(filename));
    }
    // ...
  }

似乎存在呼籲確切地是什麼需要。更明確地,然而,電話是 Directory.Exists 時,事實上,File.Exists 是什麼需要在這裡。換句話說,雖然代碼是肯定可讀的這是不正確,至少在這種情況下,避免使用靜態語法是可能會更好。

請注意是否使用的靜態指令指定為 System.IO.Directory 和 System.IO.File,在調用 Exists,強制型消歧首碼可以解析多義性的代碼時,編譯器會發出錯誤。

一個額外的功能,使用靜態指令是其行為的擴充方法。擴充方法不搬到全球範圍內,因為通常會發生與靜態方法。例如,使用靜態 ParallelEnumerable 指令納入全球範圍不會選擇的方法,你只能打電話給選擇 (檔、 (檔案名) => { ...}). 這一限制是由設計。第一,擴充方法旨在將呈現為實例方法的物件 (檔。Select((filename) => {......}) 舉個例子) 這並不是一個正常的模式來叫他們直接從該類型的靜態方法。第二,有庫 (如更改,如可枚舉和 ParallelEnumerable 有相互重疊的方法的名字一樣選擇的類型。讓所有這些類型添加到全域範圍逼IntelliSense的不必要的混亂,並可能介紹含糊不清的調用了 (雖然不是在基於更改的類)。

雖然擴充方法不會得到放到全球範圍,C# 6.0 仍然與擴充方法中使用靜態指令允許類。使用靜態指令實現相同作為使用 (命名空間) 指令並只為特定的類有針對性的使用範圍之外靜態指令。換句話說,使用靜態允許開發人員縮小什麼擴充方法都可用到的標識,而不是整個命名空間的特定類。例如,請考慮中的代碼片段圖 4

圖 4 只擴充方法從 ParallelEnumerable Are 在範圍內

using static System.Linq.ParallelEnumerable;
using static System.IO.Console;
using static System.Threading.Interlocked;
// ...
    string[] files = GetFiles(directoryPath, searchPattern,
      System.IO.SearchOption.AllDirectories);
    files.AsParallel().ForAll( (filename) =>
    {
      Encrypt(filename);
      WriteLine($"\t'{filename}' encrypted");
      Increment(ref fileCount);
    });
// ...

注意到沒有使用更改語句。取而代之的是使用靜態的 System.Linq.ParallelEnumerable 指令,從 ParallelEnumerable 作為擴充方法必須在範圍內造成隻擴充方法。像 System.Linq.Enumerable 的類上的所有擴充方法將作為擴充方法不可用。例如,檔。Select(...) 將失敗編譯,因為選擇不在範圍內的字串陣列 (或甚至 IEnumerable <字串>)。與此相反的是,AsParallel 是通過 System.Linq.ParallelEnumerable 的範圍內。 總之,使用擴充方法的類上的靜態指令會將那類的擴充方法引入作用域作為擴充方法。(相同的類上的非擴充方法將被納入全球範圍通常。)

一般情況下,最佳做法是限制使用的使用靜態指令在如 System.Console 或 System.Math (不同于平行) 範圍內重複使用的幾類。 同樣地,當使用靜態枚舉,一定枚舉項是不言自明的沒有他們的類型識別碼。例如,也許是我的喜愛,指定使用 Microsoft.VisualStudio.TestTools.UnitTesting.Assert 在單元測試檔以啟用測試斷言調用 (如不合事實、 AreEqual <T>、 失敗和 IsNotNull。

運算子名稱

圖 3 包括新 C# 6.0,名稱運算子中的另一個功能。這是一個新的上下文關鍵字來標識字串提取為一個常數 (在編譯時) 的任何識別碼指定為參數的非限定的名稱。在圖 3,nameof(filename) 返回"檔案名",該加密方法的參數的名稱。但是,名稱適用于任何程式設計識別碼。例如, 圖 5 利用名稱傳遞給 INotifyPropertyChanged.PropertyChanged 的屬性名稱。 (順便說一句,使用 CallerMemberName 屬性來檢索引發調用的屬性名稱仍是一個有效的方法,以檢索該屬性的名稱 ; 請參閱 itl.tc/?p=11661.)

圖 5 使用 INotifyPropertyChanged.PropertyChanged 運算子名稱

public class Person : INotifyPropertyChanged
{
  public event PropertyChangedEventHandler PropertyChanged;
  public Person(string name)
  {
    Name = name;
  }
  private string _Name;
  public string Name
  {
    get{ return _Name; }
    set
    {
      if (_Name != value)
      {
        _Name = value;
        PropertyChangedEventHandler propertyChanged = PropertyChanged;
        if (propertyChanged != null)
        {
          propertyChanged(this,
            new PropertyChangedEventArgs(nameof(Name)));
        }
      }
    }
  }
  // ...
}
[TestClass]
public class PersonTests
{
  [TestMethod]
  public void PropertyName()
  {
    bool called = false;
    Person person = new Person("Inigo Montoya");
    person.PropertyChanged += (sender, eventArgs) =>
    {
      AreEqual(nameof(CSharp6.Person.Name), eventArgs.PropertyName);
      called = true;
    };
    person.Name = "Princess Buttercup";
    IsTrue(called);
  }   
}

請注意是否只是不合格的"名稱"提供 (因為它是在範圍內) 或完全限定的 CSharp6.Person.Name 識別碼使用和測試,結果是只有最後的識別碼 (一個虛線的名稱的最後一個元素)。

通過利用名稱運算子,就可以消除絕大多數的引用的代碼識別碼,只要他們是在範圍內的"魔術"字串。這不僅消除了由於拼寫錯誤內的魔術的字串,這從來沒有核查過編譯器,執行階段錯誤,而且還使重構的工具,如要更新的名稱更改識別碼的所有引用重命名。而且,如果名稱更改不重構工具的情況下,編譯器將發出一個錯誤,指示該識別碼不再存在。

字串插值

圖 3 可以改善通過不僅指定的異常消息,指示該檔找不到,但也通過顯示檔案名本身。在 C# 6.0 之前, 你會這樣使用字串。為了將檔案名嵌入到字串的格式。然而,複合格式設置不是可讀性最強的。設置格式一個人的名字,例如,需要代以根據參數的順序,如中所示的預留位置圖 6 轉讓的消息。

圖 6 複合字串與字串插值格式

[TestMethod]
public void InterpolateString()
{
  Person person = new Person("Inigo", "Montoya") { Age = 42 };
  string message =
    string.Format("Hello!  My name is {0} {1} and I am {2} years old.",
    person.FirstName, person.LastName, person.Age);
  AreEqual<string>
    ("Hello!  My name is Inigo Montoya and I am 42 years old.", message);
  string messageInterpolated =
    $"Hello!  My name is {person.FirstName} {person.LastName} and I am
    {person.Age} years old.";
  AreEqual<string>(message, messageInterpolated);
}

注意到與分配給 messageInterpolated 複合格式設置一種替代方法。在此示例中,分配到 messageInterpolated 的運算式是字串加首碼"$"和花括弧標識是在字串中的嵌入的內聯的代碼。在這種情況下,人的特性用來使此字串顯著易於閱讀比一個撰寫字串。此外,字串插值語法可減少錯誤參數後的不當順序的格式字串或完全失蹤,導致異常引起的。(Visual Studio2015年預覽中還有沒有 $ 字元並且,相反,每個左花括弧需要在它之前的一條斜線。繼Visual Studio2015年預覽的版本更新,以改用在字串文本語法 $。)

在編譯時要調用的等效字串轉換字串插值。設置調用的格式。這使得地方支援以實現當地語系化的但作為前 (儘管仍與傳統的格式字串) 並沒有引入任何郵政編譯注入通過字串的代碼。

圖 7 顯示的字串插值兩個更多的例子。

圖 7 使用字串插值來替代字串。格式

public Person(string firstName, string lastName, int? age=null)
{
  Name = $"{firstName} {lastName}";
  Age = age;
}
private static void Encrypt(string filename)
{
  if (!System.IO.File.Exists(filename))
  {
    throw new ArgumentException(
      $"The file, '{filename}', does not exist.", nameof(filename));
  }
  // ...
}

請注意在第二個案件中,throw 語句,兩個字串插值名稱運算子的杠杆比率。字串插值是什麼原因導致 ArgumentException 消息以包含檔的名稱 (即,"該檔,'c:\data\missingfile.txt' 不存在")。名稱運算子的用法是確定加密參數 ("filename"),ArgumentException 建構函式的第二個參數的名稱。Visual Studio2015年是充分意識到字串插值語法中,彩色編碼和IntelliSense提供插值字串中嵌入的代碼塊。

Null 條件運算子

儘管取消了在圖 2 為清楚起見,幾乎每個接受參數的 Main 方法需要檢查 null 之前調用該長度成員來確定多少的參數中傳遞的參數。 更一般地,它是一種非常常見的模式來檢查 null 值之前調用的成員為了避免 System.NullReferenceException (這幾乎總是表明程式設計邏輯中的錯誤)。這種模式的頻率,C# 6.0 引入了"嗎?."稱為 null 條件運算子的運算子:

public static void Main(string[] args)
{
  switch (args?.Length)
  {
  // ...
  }
}

Null 條件運算子轉換為檢查是否運算元為 null 之前調用該方法或屬性 (在本例中的長度)。邏輯上等效的顯式代碼將 (儘管在 6.0 C# 語法中的參數值只計算一次):

(args != null) ? (int?)args.Length : null

Null 條件運算子特別方便的是它可以將連結。如果,例如,調用字串 [] 名稱 = 人嗎?。名稱嗎?。拆分 (' '),拆分將只會被調用,如果人與人。名稱不是空的。連結,如果第一個運算元為 null,運算式求值時,短路,並且沒有進一步調用運算式調用鏈內的會發生。當心,不過,你不要無意中忽視其他 null 條件運算子。例如,考慮名稱 = 人嗎?。Name.Split (' ').如果還有 person 實例,但名稱為空,舉出拆分調用時出現。這並不意味著您必須使用一連串的 null 條件運算子,但相反,你應該故意對邏輯。在人的情況下,例如,如果名稱進行驗證,並且決不會為 null,沒有額外的 null 條件運算子是必要。

重要的事情要注意關於 null 條件運算子是當使用傳回值類型的成員,它始終返回該類型的可以為 null 的版本。例如,args 嗎?。長度返回 int?,不只是一個整型數。雖然也許有點奇怪 (與其他經營者的行為),返回一個可以為 null 的數值型別的發生只調用鏈的末尾。其結果是,調用點 (".") 運算子在長度上的只允許對 int (不是 int?) 成員的調用。然而,封裝 args 嗎?。在括弧中的長度 — — 迫使 int 嗎? 結果通過括弧運算子優先順序 — — 將調用 int 嗎? 返回並做出一個可以為 Null 的 <T> 具體可用的成員 (HasValue 和價值)。

Null 條件運算子是個不錯的功能,在其自身上。然而,使用委託調用結合解析 C# 1.0 以來一直存在一個 C# 痛點。請注意如何在圖 5 之前檢查的值為 null,然後最後觸發事件 (引發) 的一份本機複本分配 PropertyChange 事件處理常式。這是最簡單的執行緒安全方式不用冒著危險,事件的取消訂閱將發生之間的時間檢查 null 出現和觸發事件時調用的事件。不幸的是,這是不太直觀,我經常遇到並不遵循這種模式的代碼 — — NullReferenceExceptions 不一致的結果。幸運的是,與 C# 6.0 中的 null 條件運算子的引入,解決了此問題。

與 C# 6.0 中,從更改的代碼片段:

PropertyChangedEventHandler propertyChanged = PropertyChanged;
if (propertyChanged != null)
{
  propertyChanged(this, new PropertyChangedEventArgs(nameof(Name)));
}

對簡單:

PropertyChanged?.Invoke(propertyChanged(
  this, new PropertyChangedEventArgs(nameof(Name)));

而且,因為事件只是一個委託,同一模式,調用通過 null 條件運算子和調用的委託是總是可能。這項功能,也許比任何其他在 C# 6.0 中,是一定要改變你在將來編寫的 C# 代碼的方式。一旦您利用 null 條件運算子的委託,很有可能你永遠不會回去,和舊的方式編寫代碼 (除非,當然,你深陷於 pre-C# 6.0 世界。)

Null 條件運算子也可以在索引運算子結合使用。例如,當您使用它們與 Newtonsoft.JObject 相結合,您可以遍歷一個 JSON 物件來檢索特定元素,如中所示圖 8

圖 8 的主控台顏色配置示例

string jsonText =
    @"{
      'ForegroundColor':  {
        'Error':  'Red',
        'Warning':  'Red',
        'Normal':  'Yellow',
        'Verbose':  'White'
      }
    }";
  JObject consoleColorConfiguration = JObject.Parse(jsonText);
  string colorText = consoleColorConfiguration[
    "ForegroundColor"]?["Normal"]?.Value<string>();
  ConsoleColor color;
  if (Enum.TryParse<ConsoleColor>(colorText, out color))
  {
    Console.ForegroundColor = colorText;
  }

它是重要的是注意到 JObject 是否索引無效,不同 MSCORLIB 內的大多數集合,亦不引發異常。如果,例如,ForegroundColordoesn't 存在,JObject 返回 null 而不是引發異常。這是重要的因為使用 null 條件運算子集合,拋出 IndexOutOfRangeException 幾乎永遠是多餘的和沒有這種安全存在時可能意味著安全。返回到顯示的主要和 args 實例的程式碼片段,請考慮下列情況:

public static void Main(string[] args)
{
  string directoryPath = args?[0];
  string searchPattern = args?[1];
  // ...
}

這個例子讓危險是 null 條件運算子給虛假的安全感,這意味著如果參數不為 null 然後元素必須存在。當然,這不是這樣,因為即使參數不為 null,該元素可能不存在。因為檢查元素計數與 args 嗎?。長度已經驗證參數不為 null,你從來沒有真正需要檢查長度後的集合的索引時也使用 null 條件運算子。總之,避免使用索引運算子使用 null 條件運算子的組合,如果索引運算子拋出了 IndexOutOfRangeException 的非存在索引。這樣做會導致代碼有效性虛假的安全感。

結構的預設建構函式

另一個 C# 6.0 功能,要認識到的是對數值型別的預設 (無參數) 建構函式的支援。這以前是不允許因為當初始化陣列,違約類型結構的欄位或初始化一個實例的預設運算子),不會調用的建構函式。在 C# 中 6.0 中,然而,預設建構函式現在允許帶有警告他們只調用數值型別的時候是用 new 運算子的實例。陣列初始化和顯式分配的預設值 (或結構欄位類型的隱式初始化) 將繞過預設建構函式。

若要瞭解如何利用預設建構函式,考慮該示例中所示的 ConsoleConfiguration 類圖 9。給出一個建構函式,並通過 CreateUsingNewIsInitialized 方法中所示的 new 運算子的調用,將完全初始化結構。正如你所期望的以及證明在圖 9,建構函式鏈完全支援,藉以一個建構函式可以調用另一個使用"this"關鍵字之後,建構函式聲明。

圖 9 對值型別宣告一個預設建構函式

using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
public struct ConsoleConfiguration
{
  public ConsoleConfiguration() :
    this(ConsoleColor.Red, ConsoleColor.Yellow, ConsoleColor.White)
  {
    Initialize(this);
  }
  public ConsoleConfiguration(ConsoleColor foregroundColorError,
    ConsoleColor foregroundColorInformation,
    ConsoleColor foregroundColorVerbose)
  {
    // All auto-properties and fields must be set before
    // accessing expression bodied members
    ForegroundColorError = foregroundColorError;
    ForegroundColorInformation = foregroundColorInformation;
    ForegroundColorVerbose = foregroundColorVerbose;
  }
   private static void Initialize(ConsoleConfiguration configuration)
  {
    // Load configuration from App.json.config file ...
  }
  public ConsoleColor ForegroundColorVerbose { get; }
  public ConsoleColor ForegroundColorInformation { get; }
  public ConsoleColor ForegroundColorError { get; }
  // ...
  // Equality implementation excluded for elucidation
}
[TestClass]
public class ConsoleConfigurationTests
{
  [TestMethod]
  public void DefaultObjectIsNotInitialized()
  {
    ConsoleConfiguration configuration = default(ConsoleConfiguration);
    AreEqual<ConsoleColor>(0, configuration.ForegroundColorError);
    ConsoleConfiguration[] configurations = new ConsoleConfiguration[42];
    foreach(ConsoleConfiguration item in configurations)
    {
      AreEqual<ConsoleColor>(default(ConsoleColor),
        configuration.ForegroundColorError);
      AreEqual<ConsoleColor>(default(ConsoleColor),
        configuration.ForegroundColorInformation);
      AreEqual<ConsoleColor>(default(ConsoleColor),
        configuration.ForegroundColorVerbose);
    }
  }
  [TestMethod]
  public void CreateUsingNewIsInitialized()
  {
    ConsoleConfiguration configuration = new ConsoleConfiguration();
    AreEqual<ConsoleColor>(ConsoleColor.Red,
      configuration.ForegroundColorError);
    AreEqual<ConsoleColor>(ConsoleColor.Yellow,
      configuration.ForegroundColorInformation);
    AreEqual<ConsoleColor>(ConsoleColor.White,
      configuration.ForegroundColorVerbose);
  }
}

還有關于結構要記住的一個關鍵點:所有實例欄位和自動屬性 (因為他們有支援欄位) 在調用任何其他實例成員之前,必須完全初始化。其結果是,在示例中圖 9,直到所有欄位,則建構函式不能調用初始化方法和已分配自動屬性。幸運的是,如果一個鏈式建構函式處理一切所需的初始化和通過一個"this"調用調用,編譯器就是足夠的聰明來檢測不需要初始化資料再次從正文中非此調用的建構函式,如中所示圖 9

汽車性能的改進

通知還在圖 9 (為其有沒有顯式欄位) 的三個屬性都聲明為自動屬性 (與沒有正文) 和僅 getter。這些只有吸氣的自動屬性是由一個唯讀的欄位 (內部) 支援的唯讀屬性聲明的 C# 6.0 功能。因此,這些屬性可以只從修改的建構函式內。

只有 getter 自動屬性是可用在結構和類聲明中,但它們特別重要的是結構由於最佳實踐指南的結構是不變的。這一切被需要而不是行六左右需要聲明為唯讀的屬性並將其初始化 C# 6.0,現在一個單行聲明之前,從建構函式中的賦值。因此,宣言 》 的不可變結構現在不是只有正確的程式設計模式的結構,但也更簡單的模式 — — 大加讚賞變化從事先語法正確編碼需要更多的努力。

C# 6.0 中引入第二個自動屬性功能是支援初始值設定項。舉個例子,我可以向 ConsoleConfiguration,帶有初始值設定項中添加一個靜態的 DefaultConfig 自動屬性:

// Instance property initialization not allowed on structs.
static private Lazy<ConsoleConfiguration> DefaultConfig{ get; } =
  new Lazy<ConsoleConfiguration>(() => new ConsoleConfiguration());

此類屬性為訪問預設 ConsoleConfigurtion 實例提供單個實例原廠模式。注意到這一點,而不是分配 getter,僅從自動屬性在建構函式中,本示例利用 System.Lazy <T> 並具現化它作為初始值設定項聲明期間。其結果是,一旦該建構函式完成後,該實例懶 <ConsoleConfiguration> 將永恆不變,對 DefaultConfig 的調用將始終返回同一個實例,ConsoleConfiguration。

請注意,自動屬性初始值設定項不允許對結構的實例成員 (雖然他們當然允許在類上)。

運算式體方法和自動屬性

C# 6.0 中引入的另一個特點是表達酒體成員。此功能存在的屬性和方法,並允許使用箭頭運算子 (=>) 來將運算式分配給屬性或方法代替語句體。例如,因為私人和懶惰 <T> 類型的前面的示例中的 DefaultConfig 屬性,檢索的實際預設實例的 ConsoleConfiguration 要求 GetDefault 方法:

static public ConsoleConfiguration GetDefault() => DefaultConfig.Value;

然而,在這個片段中,注意有沒有語句塊類型方法體。相反,此方法被實現與只 (而不是陳述) 作為首碼的運算式與 lambda 箭頭操作符。目的是提供一種簡單的一行執行所有的儀式,而是功能性的有或沒有參數在方法簽名中的一個:

private static void LogExceptions(ReadOnlyCollection<Exception> innerExceptions) =>
  LogExceptionsAsync(innerExceptions).Wait();

對於屬性,請注意表達機構工作只為唯讀的 (只有 getter) 屬性。事實上,語法是幾乎完全相同表達的健全方法只是有沒有識別碼後面的括弧。回到前面的示例中的人,我可以實現與使用表達機構的唯讀的名字和姓氏屬性中所示圖 10

圖 10 表達體自動屬性

public class Person
{
  public Person(string name)
  {
    Name = name;
  }
  public Person(string firstName, string lastName)
  {
    Name = $"{firstName} {lastName}";
    Age = age;
  }
  // Validation ommitted for elucidation
  public string Name {get; set; }
  public string FirstName => Name.Split(' ')[0];
  public string LastName => Name.Split(' ')[1];
  public override string ToString() => "\{Name}(\{Age}";
}

此外,還可以在索引成員,例如從內部集合,返回一個專案上使用表達濃郁屬性。

字典初始值設定項

字典類型集合是用於定義一個名稱值對的重要手段。不幸的是,用於初始化語法是有點次優的:

{ {"First", "Value1"}, {"Second", "Value2"}, {"Third", "Value3"} }

為改善這種情況,C# 6.0 包括新字典賦數值型別的語法:

Dictionary<string, Action<ConsoleColor>> colorMap =
  new Dictionary<string, Action<ConsoleColor>>
{
  ["Error"] =               ConsoleColor.Red,
  ["Information"] =        ConsoleColor.Yellow,
  ["Verbose"] =            ConsoleColor.White
};

為了提高語法,語言團隊介紹指派運算子作為關聯一雙使查找 (名稱) 值對或映射的專案的一種手段。查找是任何索引值 (和資料類型) 聲明的字典要。

異常的改進

不甘示弱,例外情況也有幾個小語種的調整,在 C# 6.0 中。首先,它是現在可以使用等待內既抓住和最後塊中的子句中所示圖 11

圖 11 使用 Catch 內等待著,最後塊

public static async Task<int> EncryptFilesAsync(string directoryPath, string searchPattern = "*")
{
  ConsoleColor color = Console.ForegroundColor;
  try
  {
  // ...
  }
  catch (System.ComponentModel.Win32Exception exception)
    if (exception.NativeErrorCode == 0x00042)
  {
    // ...
  }
  catch (AggregateException exception)
  {
    await LogExceptionsAsync(exception.InnerExceptions);
  }
  finally
  {
    Console.ForegroundColor = color;
    await RemoveTemporaryFilesAsync();
  }
}

自推出以來在 C# 5.0 下等待著,支援等待在抓捕和 finally 塊原來更尋求後比最初預期。例如,從 catch 或最後塊中的調用非同步方法的模式是相當常見的特別是當它來到清理或記錄在這段時間。現在,在 C# 6.0 中,它是最後可能。

第二個的例外相關特徵 (它自問世以來在Visual Basic1.0) 是支援異常篩選器,這樣在按特定異常類型篩選,它是現在可以指定如果子句來進一步限制是否或不將會由 catch 塊捕獲異常。(有時此功能也被利用的副作用,例如日誌記錄異常作為例外"飛的"而不實際執行任何異常處理。)請注意要注意有關此功能就是,如果有任何的機會可能會當地語系化您的應用程式,避免漁行經異常消息,因為他們會不再工作無需更改以下當地語系化的條件運算式。

總結

所有 C# 6.0 功能提的最後一點是,儘管他們顯然需要 6.0 C# 編譯器,附帶Visual Studio2015年或晚些時候,他們不需要更新的版本的 Microsoft.NET 框架。因此,你可以使用 C# 6.0 功能,即使您正在編譯針對.NET 框架 4,舉個例子。這是可能的原因是所有功能在編譯器中實現,而且沒有任何.NET 框架依賴項。

與那旋風之旅,我總結了我看看 C# 6.0。不討論的只有兩個剩餘功能是支援定義自訂的添加擴充方法,以說明集合初始值設定項,和一些小的但是改進的重載解析。總之,C# 6.0 不會從根本上改變您的代碼,至少不礙事泛型或LINQ做。它做什麼,然而,是做出正確的編碼模式更簡單。委託的 null 條件運算子可能是最好的例子,但如此,也是很多其他的功能包括字串插值、 名稱運算子和汽車性能改進 (尤其是對於唯讀屬性)。

對於其他資訊這裡有幾個額外的引用:

此外,雖然不可用直到 2015 年第二季度,尋找我的書,至下一版"基本 C# 6.0"(intellitect.com/EssentialCSharp)。

由你讀到這篇文章時,C# 6.0 功能討論可能會關閉。然而,很少有人懷疑出現了新的微軟,一個致力於投資于跨平臺發展使用開放源碼允許開發者社區中創造偉大的軟體共用的最佳做法。出於這個原因,你很快就會能夠瞭解關於 C# 7.0,早期設計討論因為這次討論將在一個開放源碼論壇。


Mark Michaelis (itl.tc/Mark) 是 IntelliTect 的創始人。他還擔任了首席技術架構師和培訓師。1996 年以來,他一直為 C#、Visual StudioTeam System(VSTS) 和Windows SDK,Microsoft MVP,他被公認為微軟區域主任在 2007 年。 他還擔任幾個 Microsoft 軟體設計­審評組,包括 C#、 連接系統分部和 VSTS。米迦勒在開發者會議上講話、 寫了許多文章和書籍,和目前正在對下一版的"基本 C#"(艾迪生-Wesley 專業)。

感謝以下的微軟技術專家對本文的審閱:麥斯 Torgersen