国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

微軟CONNECT(); 2016C#7.0新特性

2019-11-11 02:19:11
字體:
供稿:網(wǎng)友

CONNECT(); 2016第 31 卷,第 12 期

.NET Framework - C# 7.0 中的新增功能

作者 Mark Michaelis | Connect(); 2016 | 獲取代碼早在 2015 年 12 月,我就談?wù)撨^ C# 7.0 的設(shè)計(jì) (msdn.com/magazine/mt595758)。在過去的一年里發(fā)生了很多變化,但是該團(tuán)隊(duì)現(xiàn)在正致力于 C# 7.0 的開發(fā),預(yù)期 Visual Studio 2017 候選發(fā)布版本將實(shí)現(xiàn)幾乎所有的新功能。(我之所以說是幾乎所有,是因?yàn)樵?Visual Studio 2017 實(shí)際交付之前,仍可能會(huì)有進(jìn)一步的變化。) 如需簡要概述,可以查看 itl.tc/CSharp7FeatureSummary 上的匯總表。在本文中,我將逐一詳細(xì)介紹這些新功能。

解構(gòu)函數(shù)

從 C# 1.0 開始,就能調(diào)用函數(shù),就是將參數(shù)組合起來并封裝到一個(gè)類中的構(gòu)造函數(shù)。但是,從來沒有一種簡便的方式可將對(duì)象解構(gòu)回其各個(gè)組成部分。例如,假設(shè)有一個(gè) PathInfo 類,它采用文件名的每個(gè)元素(目錄名、文件名、擴(kuò)展名),并將它們組合成一個(gè)對(duì)象,然后支持操作對(duì)象的不同元素。現(xiàn)在,假設(shè)你需要將該對(duì)象提取(解構(gòu))回其各個(gè)組成部分。在 C# 7.0 中,通過解構(gòu)函數(shù)完成這項(xiàng)任務(wù)將變得輕而易舉,解構(gòu)函數(shù)可返回對(duì)象的具體確定組件。注意,不要將解構(gòu)函數(shù) (deconstructor) 與析構(gòu)函數(shù) (destructor)(確定性對(duì)象解除分配和清除)或終結(jié)器 (itl.tc/CSharpFinalizers) 混淆。我們來看看圖 1 中的 PathInfo 類。圖 1 具有解構(gòu)函數(shù)的 PathInfo 類及相關(guān)測試
public class PathInfo{  public string DirectoryName { get; }  public string FileName { get; }  public string Extension { get; }  public string Path  {    get    {      return System.IO.Path.Combine(        DirectoryName, FileName, Extension);    }  }  public PathInfo(string path)  {    DirectoryName = System.IO.Path.GetDirectoryName(path);    FileName = System.IO.Path.GetFileNameWithoutExtension(path);    Extension = System.IO.Path.GetExtension(path);  }  public void Deconstruct(    out string directoryName, out string fileName, out string extension)  {    directoryName = DirectoryName;    fileName = FileName;    extension = Extension;  }  // ...}顯然,可以和在 C# 1.0 一樣調(diào)用 Deconstruct 方法。但是,C# 7.0 提供了可以顯著簡化調(diào)用的語法糖。如果存在解構(gòu)函數(shù)的聲明,則可以使用新的 C# 7.0“類似元組”的語法調(diào)用它(參見圖 2)。圖 2 解構(gòu)函數(shù)調(diào)用和賦值
PathInfo pathInfo = new PathInfo(@"//test/unc/path/to/something.ext");{  // Example 1: Deconstructing declaration and assignment.  (string directoryName, string fileName, string extension) = pathInfo;  VerifyExpectedValue(directoryName, fileName, extension);}{  string directoryName, fileName, extension = null;  // Example 2: Deconstructing assignment.  (directoryName, fileName, extension) = pathInfo;  VerifyExpectedValue(directoryName, fileName, extension);}{  // Example 3: Deconstructing declaration and assignment with var.  var (directoryName, fileName, extension) = pathInfo;  VerifyExpectedValue(directoryName, fileName, extension);}請(qǐng)注意,C# 第一次如何允許同時(shí)向不同值的多個(gè)變量賦值。這與將所有變量都初始化為同一值 (null) 的空賦值聲明不同:
string directoryName, filename, extension = null;通過新的類似元組的語法,賦予每個(gè)變量一個(gè)不同的值,該值與其名稱不對(duì)應(yīng),但與它出現(xiàn)在聲明和解構(gòu)語句中的順序相對(duì)應(yīng)。正如你所期望的,out 參數(shù)的類型必須與被分配的變量類型相匹配,并且允許使用 var,因?yàn)榇祟愋涂梢詮?Deconstruct 參數(shù)類型中推斷出來。但是,請(qǐng)注意,雖然可以在圓括號(hào)外面放置一個(gè) var(如圖 2 中的示例 3 所示),但此時(shí)即使所有變量的類型均相同,也不能拉出字符串。請(qǐng)注意,此時(shí) C# 7.0 類似元組的語法要求圓括號(hào)內(nèi)至少出現(xiàn)兩個(gè)變量。例如,即使存在類似如下的解構(gòu)函數(shù),也不允許使用 (FileInfo path) = pathInfo;:
public void Deconstruct(out FileInfo file)換句話說,不能對(duì)僅有一個(gè) out 參數(shù)的 Deconstruct 方法使用 C# 7.0 解構(gòu)函數(shù)。

使用元組

正如我所說過的,前面的每個(gè)示例都利用了 C# 7.0 類似元組的語法。此類語法的特點(diǎn)就是用圓括號(hào)括住分配的多個(gè)變量(或?qū)傩裕N抑允褂眯g(shù)語“類似元組的”,是因?yàn)樗羞@些解構(gòu)函數(shù)示例實(shí)際上在內(nèi)部均未使用任何元組類型。(實(shí)際上,由于已分配的對(duì)象是表示封裝的組成部分的實(shí)例,因此,不允許通過解構(gòu)函數(shù)語法分配元組,也可以說這樣做不太必要。)借助 C# 7.0,現(xiàn)在有了一種特別簡化的語法,可以使用元組,如圖 3 所示。只要允許使用類型說明符,就可以使用這種語法,其中包括聲明、強(qiáng)制轉(zhuǎn)換運(yùn)算符和類型參數(shù)。圖 3 聲明、實(shí)例化并使用 C# 7.0 元組語法
[TestMethod]public void Constructor_CreateTuple(){  (string DirectoryName, string FileName, string Extension) pathData =    (DirectoryName: @"//test/unc/path/to",    FileName: "something",    Extension: ".ext");  Assert.AreEqual<string>(    @"//test/unc/path/to", pathData.DirectoryName);  Assert.AreEqual<string>(    "something", pathData.FileName);  Assert.AreEqual<string>(    ".ext", pathData.Extension);  Assert.AreEqual<(string DirectoryName, string FileName, string Extension)>(    (DirectoryName: @"//test/unc/path/to",      FileName: "something", Extension: ".ext"),    (pathData));  Assert.AreEqual<(string DirectoryName, string FileName, string Extension)>(    (@"//test/unc/path/to", "something", ".ext"),    (pathData));  Assert.AreEqual<(string, string, string)>(    (@"//test/unc/path/to", "something", ".ext"), (pathData));  Assert.AreEqual<Type>(    typeof(ValueTuple<string, string, string>), pathData.GetType());}[TestMethod]public void ValueTuple_GivenNamedTuple_ItemXHasSameValuesAsNames(){  var normalizedPath =    (DirectoryName: @"//test/unc/path/to", FileName: "something",    Extension: ".ext");  Assert.AreEqual<string>(normalizedPath.Item1, normalizedPath.DirectoryName);  Assert.AreEqual<string>(normalizedPath.Item2, normalizedPath.FileName);  Assert.AreEqual<string>(normalizedPath.Item3, normalizedPath.Extension);}static public (string DirectoryName, string FileName, string Extension)  SplitPath(string path){  // See http://bit.ly/2dmJIMm Normalize method for full implementation.  return (              System.IO.Path.GetDirectoryName(path),    System.IO.Path.GetFileNameWithoutExtension(path),    System.IO.Path.GetExtension(path)    );}如果你不太熟悉元組,可以在輕量級(jí)語法中將多個(gè)類型組合成一個(gè)包含類型,然后在對(duì)其進(jìn)行實(shí)例化的方法外面使用。之所以說是輕量級(jí),是因?yàn)楹投x類/結(jié)構(gòu)不同,元組可通過內(nèi)聯(lián)和動(dòng)態(tài)方式“聲明”。但是,與也支持內(nèi)聯(lián)聲明和實(shí)例化的動(dòng)態(tài)類型不同,元組可以從其包含成員的外部訪問,它們實(shí)際上可以包含在 API 中。雖然外部 API 支持,但元組沒有兼容版本的擴(kuò)展(除非類型參數(shù)本身正好支持推導(dǎo)),因此,在公共 API 中應(yīng)謹(jǐn)慎使用。因此,更好的辦法是對(duì)公共 API 中的返回內(nèi)容使用標(biāo)準(zhǔn)類。在 C# 7.0 之前,該框架已有元組類 System.Tuple<…>(在 Microsoft .NET Framework 4 中引入)。但 C# 7.0 與之前的解決方案不同,因?yàn)樗鼘⒄Z義意圖嵌入到聲明中并引入一個(gè)元組值類型:  System.ValueTuple<…>。我們現(xiàn)在來看看語義意圖。請(qǐng)注意,在圖 3 中,C# 7.0 元組語法可讓你為元組包含的每個(gè) ItemX 元素聲明別名。例如,圖 3 中的 pathData 元組實(shí)例已定義強(qiáng)類型 DirectoryName: string、FileName: string 和 Extension: string 屬性,因此,可以調(diào)用(例如)pathData.DirectoryName。這是一項(xiàng)重大改進(jìn),因?yàn)樵?C# 7.0 之前,唯一可用的名稱是 ItemX 名稱,其中 X 將針對(duì)每個(gè)元素增加。現(xiàn)在,雖然 C# 7.0 元組的元素屬于強(qiáng)類型,但這些名稱本身在類型定義中并未區(qū)分。因此,可以分配兩個(gè)使用不同別名的元組,你將得到一條警告,通知你將忽略右邊的名稱:
// Warning: The tuple element name 'AltDirectoryName1' is ignored// because a different name is specified by the target type...(string DirectoryName, string FileName, string Extension) pathData =  (AltDirectoryName1: @"//test/unc/path/to",  FileName: "something", Extension: ".ext");同樣,可以將元組分配到尚未定義部分別名元素名稱的其他元組:
// Warning: The tuple element name 'directoryName', 'FileNAme' and 'Extension'// are ignored because a different name is specified by the target type...(string, string, string) pathData =  (DirectoryName: @"//test/unc/path/to", FileName: "something", Extension: ".ext");必須確定,每個(gè)元素的類型和順序都定義類型兼容性。僅忽略元素名稱。然而,即使在名稱不同時(shí)被忽略,它們?nèi)匀辉?IDE 中提供 IntelliSense。請(qǐng)注意,無論是否定義元素名稱的別名,所有元組均有 ItemX 名稱,其中 X 對(duì)應(yīng)于元素的數(shù)量。ItemX 名稱很重要,因?yàn)樗鼈兪窃M從 C# 6.0 開始起可用,即使沒有別名元素的名稱也是如此。需要注意的另一點(diǎn)就是,基礎(chǔ) C# 7.0 元組類型是 System.ValueTuple。如果正針對(duì)其進(jìn)行編譯的框架中未提供此類型,可以通過 NuGet 包訪問它。有關(guān)元組內(nèi)部元素的詳細(xì)信息,請(qǐng)參閱 intellitect.com/csharp7tupleiinternals。

具有 Is 表達(dá)式的模式匹配

有時(shí)會(huì)存在基類(例如 Storage),以及一系列的派生類、DVD、UsbKey、HardDrive、FloppyDrive 等。要對(duì)每個(gè)類實(shí)施 Eject 方法,請(qǐng)使用以下多個(gè)選項(xiàng):As 運(yùn)算符使用 As 運(yùn)算符轉(zhuǎn)換并賦值檢查結(jié)果是否為 null執(zhí)行 eject 操作Is 運(yùn)算符使用 Is 運(yùn)算符檢查類型轉(zhuǎn)換類型并為其賦值執(zhí)行 eject 操作Cast顯式轉(zhuǎn)換并賦值捕獲可能的異常執(zhí)行操作看起來不怎么樣啊!還有第四種、效果更好的方法,即使用你通過虛擬函數(shù)分派的多形性。但是,僅在具有 Storage 類的源代碼并且可以添加 Eject 方法時(shí),才可以使用這種方法。我假設(shè)的選項(xiàng)不適用于這個(gè)討論,因此需要模式匹配。上述這些方法存在的問題都是語法相當(dāng)冗長,總是要求為需要轉(zhuǎn)換的每個(gè)類提供多個(gè)語句。C# 7.0 提供模式匹配,用作一種將測試和賦值合并為單個(gè)操作的方法。因此,圖 4 中的代碼簡化為如圖 5 中所示的代碼。圖 4 無模式匹配的類型轉(zhuǎn)換
// Eject without pattern matching.public void Eject(Storage storage){  if (storage == null)  {    throw new ArgumentNullException();  }  if (storage is UsbKey)  {    UsbKey usbKey = (UsbKey)storage;    if (usbKey.IsPluggedIn)    {      usbKey.Unload();      Console.WriteLine("USB Drive Unloaded.");    }    else throw new NotImplementedException();    }  else if(storage is DVD)  // ...  else throw new NotImplementedException();}圖 5 有模式匹配的類型轉(zhuǎn)換
// Eject with pattern matching.public void Eject(Storage storage){  if (storage is null)  {    throw new ArgumentNullException();  }  if ((storage is UsbKey usbDrive) && usbDrive.IsPluggedIn)  {    usbDrive.Unload();    Console.WriteLine("USB Drive Unloaded.");  }  else if (storage is DVD dvd && dvd.IsInserted)  // ...  else throw new NotImplementedException();  // Default}這兩種轉(zhuǎn)換方式的區(qū)別并不重要,但如果要經(jīng)常執(zhí)行(例如,針對(duì)每個(gè)派生類型),則前一種語法存在一種繁瑣的 C# 特性。C# 7.0 的改進(jìn)之處是將類型測試、聲明和賦值組合為一個(gè)操作,呈現(xiàn)早期的語法,但不推薦使用。在前一種語法中,檢查類型而不分配標(biāo)識(shí)符會(huì)導(dǎo)致失敗而恢復(fù)“默認(rèn)設(shè)置”,否則會(huì)很麻煩。相比之下,除了類型檢查,分配還考慮到其他條件。請(qǐng)注意,圖 5 中的代碼開始模式匹配 is 運(yùn)算符,也支持 null 比較運(yùn)算符:
if (storage is null) { ... }

使用 Switch 語句的模式匹配

雖然支持使用 is 運(yùn)算符的模式匹配實(shí)現(xiàn)了改進(jìn),但 switch 語句的模式匹配支持無疑更重要,至少在有多個(gè)可轉(zhuǎn)換的兼容類型時(shí)如此。這是因?yàn)?C# 7.0 包括 case 語句和模式匹配,此外,如果滿足 case 語句中的類型模式,就可以在 case 語句中提供、分配和訪問標(biāo)識(shí)符。圖 6 提供了一個(gè)示例。圖 6 Switch 語句中的模式匹配
public void Eject(Storage storage){  switch(storage)  {    case UsbKey usbKey when usbKey.IsPluggedIn:      usbKey.Unload();      Console.WriteLine("USB Drive Unloaded.");      break;    case DVD dvd when dvd.IsInserted:      dvd.Eject();      break;    case HardDrive hardDrive:      throw new InvalidOperationException();    case null:    default:      throw new ArgumentNullException();  }}在該示例中,請(qǐng)注意如何在 case 語句中自動(dòng)聲明和分配如 usbKey 和 dvd 的局部變量。正如你所期望的,范圍僅限于 case 語句中。但也許與變量聲明和賦值一樣重要的是附加條件,可以用一個(gè) when 子句附加到 case 語句。結(jié)果是 case 語句完全可以篩選無效的方案,無需在 case 語句內(nèi)部使用額外的篩選器。這帶來額外的好處是:如果事實(shí)上沒有完全滿足前一個(gè) case 語句,也允許計(jì)算下一個(gè) case 語句。這也意味著 case 語句不再僅限于常量,此外,switch 表達(dá)式可以是任何類型,不再僅限于 bool、char、string、integral 和 enum。新的 C# 7.0 模式匹配 switch 語句功能引入的另一個(gè)重要特征就是,case 語句順序很重要并在編譯時(shí)驗(yàn)證。(這與該語言的早期版本形成對(duì)比,早期版本中沒有模式匹配,case 語句順序也不重要。) 例如,如果我在派生自 Storage 的模式匹配 case 語句之前引入了 Storage 的 case 語句(UsbKey、DVD 和 HardDrive),則 case Storage 會(huì)隱藏所有其他的類型模式匹配(派生自 Storage)。如果 case 語句來自隱藏計(jì)算結(jié)果中的其他派生類型 case 語句的基類,將導(dǎo)致隱藏的 case 語句中出現(xiàn)編譯錯(cuò)誤。這樣,case 語句順序要求就類似于 catch 語句。讀者將會(huì)記得 null 值中的 is 運(yùn)算符返回 false。因此,對(duì)于值 null 的 switch 表達(dá)式,類型模式匹配 case 語句不匹配。為此,null case 語句的順序無關(guān)緊要;此行為在模式匹配之前與 switch 語句匹配。此外,為了支持與 C# 7.0 之前的 switch 語句的兼容性,默認(rèn)總是最后評(píng)估 case,而不考慮它出現(xiàn)在 case 語句順序中的位置。(也就是說,由于 case 總是在最后評(píng)估,可讀性通常也會(huì)將它放在最后。) 此外,goto case 語句仍僅適用于常量 case 標(biāo)簽,不適用于模式匹配。

本地函數(shù)

雖然已經(jīng)可以聲明委托并為其分配一個(gè)表達(dá)式,但是 C# 7.0 通過允許在另一個(gè)成員內(nèi)部完全聲明本地函數(shù),做出了進(jìn)一步改進(jìn)。請(qǐng)考慮圖 7 中的 IsPalindrome 函數(shù)。圖 7 本地函數(shù)示例
bool IsPalindrome(string text){  if (string.IsNullOrWhiteSpace(text)) return false;  bool LocalIsPalindrome(string target)  {    target = target.Trim();  // Start by removing any surrounding whitespace.    if (target.Length <= 1) return true;    else    {      return char.ToLower(target[0]) ==        char.ToLower(target[target.Length - 1]) &&        LocalIsPalindrome(          target.Substring(1, target.Length - 2));    }  }  return LocalIsPalindrome(text);}在該實(shí)現(xiàn)中,我先檢查傳遞到 IsPalindrome 的參數(shù)不是 null 或僅為空格。(我已使用模式匹配與 “text is null” 進(jìn)行 null 檢查。) 接下來,我聲明函數(shù) LocalIsPalindrome,其中,我以遞歸方式將第一個(gè)和最后一個(gè)字符進(jìn)行比較。這種方法的好處是,我不在可能會(huì)錯(cuò)誤調(diào)用的類范圍內(nèi)聲明 LocalIsPalindrome,進(jìn)而繞過 IsNullOrWhiteSpace 檢查。換句話說,本地函數(shù)提供其他的范圍限制,但僅在周圍函數(shù)內(nèi)部。圖 7 中的參數(shù)驗(yàn)證方案是一種通用的本地函數(shù)用例。我經(jīng)常遇到的另一個(gè)方案發(fā)生在單元測試內(nèi),例如在測試 IsPalindrome 函數(shù)時(shí)(參見圖 8)。圖 8 單元測試通常使用本地函數(shù)
[TestMethod]public void IsPalindrome_GivenPalindrome_ReturnsTrue(){  void AssertIsPalindrome(string text)  {    Assert.IsTrue(IsPalindrome(text),      $"'{text}' was not a Palindrome.");  }  AssertIsPalindrome("7");  AssertIsPalindrome("4X4");  AssertIsPalindrome("   tnt");  AssertIsPalindrome("Was it a car or a cat I saw");  AssertIsPalindrome("Never odd or even");}返回 IEnumerable<T> 的 Iterator 函數(shù)以及 yield 返回元素是另一種通用的本地函數(shù)用例。作為對(duì)該主題的總結(jié),以下列出了大家需要注意的有關(guān)本地函數(shù)的幾個(gè)要點(diǎn):本地函數(shù)不允許使用可訪問性修飾符(public、PRivate、protected)。本地函數(shù)不支持重載。即使簽名未重疊,也不能在名稱相同的同一種方法中使用兩個(gè)本地函數(shù)。編譯器將針對(duì)永不調(diào)用的本地函數(shù)發(fā)出警告。本地函數(shù)可以訪問封閉范圍內(nèi)的所有變量,包括局部變量。此行為與本地定義的 lambda 表達(dá)式相同,除了本地函數(shù)不分配表示結(jié)束的對(duì)象外,其他方面都與本地定義的 lambda 表達(dá)式相同。本地函數(shù)存在于整個(gè)方法的范圍內(nèi),而不考慮是在聲明之前還是之后調(diào)用它們。

通過引用返回

從 C# 1.0 開始,可以通過引用 (ref) 將參數(shù)傳遞給函數(shù)。結(jié)果就是對(duì)參數(shù)本身的任何改變都將傳回給調(diào)用方。請(qǐng)考慮以下 Swap 功能:
static void Swap(ref string x, ref string y)在這種情況下,被調(diào)用方法可以用新值更新原始調(diào)用方的變量,從而交換第一和第二參數(shù)中存儲(chǔ)的內(nèi)容。從 C# 7.0 開始,除了 ref 參數(shù),還可以通過函數(shù)返回傳回一個(gè)引用。例如,考慮返回圖像中與紅眼相關(guān)聯(lián)的第一像素的函數(shù),如圖 9 所示。圖 9 Ref 返回和 Ref 局部聲明
public ref byte FindFirstRedEyePixel(byte[] image){  //// Do fancy image detection perhaps with machine learning.  for (int counter = 0; counter < image.Length; counter++)  {    if(image[counter] == (byte)ConsoleColor.Red)    {      return ref image[counter];    }  }  throw new InvalidOperationException("No pixels are red.");}[TestMethod]public void FindFirstRedEyePixel_GivenRedPixels_ReturnFirst(){  byte[] image;  // Load image.  // ...    // Obtain a reference to the first red pixel.  ref byte redPixel = ref FindFirstRedEyePixel(image);  // Update it to be Black.  redPixel = (byte)ConsoleColor.Black;  Assert.AreEqual<byte>((byte)ConsoleColor.Black, image[redItems[0]]);}通過返回圖像引用,調(diào)用方然后能夠?qū)⑾袼馗聻椴煌念伾Mㄟ^數(shù)組檢查更新時(shí)發(fā)現(xiàn),該值現(xiàn)在為 black。使用 by reference 參數(shù)的替代方法如下所示,有人可能會(huì)說這種方法不太明顯、可讀性較低:
public bool FindFirstRedEyePixel(ref byte pixel);通過引用返回有兩個(gè)重要的限制,并且這兩個(gè)限制都由對(duì)象生命周期造成。對(duì)象引用不應(yīng)被視為垃圾收集,因?yàn)閷?duì)象仍然被引用,當(dāng)它們不再有任何引用時(shí),不應(yīng)消耗內(nèi)存。首先,只能返回以下內(nèi)容的引用:字段、其他引用返回屬性或函數(shù),或作為參數(shù)傳遞到引用返回函數(shù)的對(duì)象。例如,F(xiàn)indFirst-RedEyePixel 返回對(duì)圖像數(shù)組中項(xiàng)目的引用,它是函數(shù)的參數(shù)。同樣,如果圖像存儲(chǔ)為類中的字段,則可以通過引用返回該字段:
byte[] _Image;public ref byte[] Image { get {  return ref _Image; } }其次,ref 局部變量初始化為內(nèi)存中的某個(gè)存儲(chǔ)位置,且不能修改為指向不同的位置。(不能具有指向一個(gè)引用的指針和修改引用 - 對(duì)于那些有 C++ 背景的人,是指向指針的指針。)以下是需要了解的幾個(gè)按引用返回特征: 如果你要返回一個(gè)引用,則顯然必須返回。因此,這意味著在圖 9 的示例中,即使沒有紅眼像素存在,仍需要返回 ref 字節(jié)。唯一的解決方法是引發(fā)一個(gè)異常。相比之下,by reference 參數(shù)方法可讓你保持參數(shù)不變,并返回一個(gè)指示成功的布爾值。在許多情況下,這種方法可能更可取。聲明一個(gè)引用局部變量時(shí),需要初始化。這涉及到為它分配從函數(shù)或引用返回到變量的 ref:
ref string text;  // Error雖然可以在 C# 7.0 中聲明引用局部變量,但不允許聲明 ref 類型的字段:
class Thing { ref string _Text;  /* Error */ }不能為自動(dòng)實(shí)施的屬性聲明 by reference 類型:
class Thing { ref string Text { get;set; }  /* Error */ }允許使用返回引用的屬性:
class Thing { string _Text = "Inigo Montoya";   ref string Text { get { return ref _Text; } } }不能使用值(如 null 或常量)初始化引用局部變量。它必須通過返回成員或局部變量/字段的 by reference 分配: 
ref int number = null; ref int number = 42;  // ERROR

輸出變量

從 C# 的第一個(gè)版本開始,調(diào)用包含輸出參數(shù)的方法時(shí),始終要求在調(diào)用方法之前預(yù)先聲明輸出參數(shù)標(biāo)識(shí)符。但 C# 7.0 刪除了這個(gè)特性,并且允許以內(nèi)聯(lián)方式聲明輸出參數(shù)以及方法調(diào)用。圖 10 顯示了一個(gè)例子。圖 10 輸出參數(shù)的內(nèi)聯(lián)聲明
public long DivideWithRemainder(  long numerator, long denominator, out long remainder){  remainder = numerator % denominator;  return (numerator / denominator);}[TestMethod]public void DivideTest(){  Assert.AreEqual<long>(21,    DivideWithRemainder(42, 2, out long remainder));  Assert.AreEqual<long>(0, remainder);}請(qǐng)注意,在 DivideTest 方法中,從測試中對(duì) DivideWithRemainder 的調(diào)用如何在 out 修飾符之后包含一個(gè)類型說明符。此外,了解剩余部分如何自動(dòng)繼續(xù)包含在方法的范圍內(nèi),如第二個(gè) Assert.AreEqual 調(diào)用證明。很好!

文本改進(jìn)

與以前的版本不同,C# 7.0 包含數(shù)字二進(jìn)制文本格式,如下例所示:
long LargestSquareNumberUsingAllDigits =  0b0010_0100_1000_1111_0110_1101_1100_0010_0100;  // 9,814,072,356long MaxInt64 { get; } =  9_223_372_036_854_775_807;  // Equivalent to long.MaxValue還要注意對(duì)下劃線 “_” 用作數(shù)字分隔符的支持。它只是用來提高可讀性,可以放在數(shù)字位數(shù)(二進(jìn)制、十進(jìn)制或十六進(jìn)制數(shù)字)之間的任何位置。

通用的異步返回類型

有時(shí)在實(shí)施異步方法時(shí),能夠同步返回結(jié)果,縮短一個(gè)長時(shí)間運(yùn)行的操作,因?yàn)榻Y(jié)果幾乎是瞬時(shí)的,甚至是已知的。例如,考慮一個(gè)異步方法,用于確定目錄 (bit.ly/2dExeDG) 中文件的總大小。事實(shí)上,如果該目錄中沒有文件,則該方法可以立即返回,而不執(zhí)行長時(shí)間運(yùn)行的操作。直到 C# 7.0,異步語法的要求規(guī)定此類方法的返回結(jié)果應(yīng)當(dāng)是 Task<long>,因此,即使不需要這樣的 Task 實(shí)例,也要實(shí)例化 Task。(要實(shí)現(xiàn)這一點(diǎn),通用模式是從 Task.FromResult<T> 返回結(jié)果。)在 C# 7.0 中,編譯器不再限制異步方法返回到 void、Task 或 Task<T>。現(xiàn)在可以定義自定義類型,例如 .NET Core Framework 提供的 System.Threading.Tasks.ValueTask<T> struct,它們與異步方法返回值兼容。有關(guān)更多信息,請(qǐng)參閱 itl.tc/GeneralizedAsyncReturnTypes。

更多的 Expression-Bodied 成員

C# 6.0 引入了函數(shù)和屬性的 expression-bodied 成員,從而簡化了實(shí)現(xiàn)瑣碎的方法和屬性的語法。在 C# 7.0 中,將 expression-bodied 實(shí)現(xiàn)添加到了構(gòu)造函數(shù)、訪問器(get 和 set 屬性實(shí)現(xiàn)),甚至終結(jié)器中(請(qǐng)參見圖 11)。圖 11 在訪問器和構(gòu)造函數(shù)中使用 Expression-Bodied 成員
class TemporaryFile  // Full IDisposible implementation                     // left off for elucidation.{  public TemporaryFile(string fileName) =>    File = new FileInfo(fileName);  ~TemporaryFile() => Dispose();  Fileinfo _File;  public FileInfo File  {    get => _File;    private set => _File = value;  }  void Dispose() => File?.Delete();}我希望使用 expression-bodied 成員,這對(duì)于終結(jié)器特別常見,因?yàn)樽畛R姷膶?shí)現(xiàn)是調(diào)用 Dispose 方法,如上圖所示。我很高興地在此說明,對(duì) expression-bodied 成員的額外支持是由 C# 社區(qū)實(shí)施的,而不是 Microsoft C# 團(tuán)隊(duì)。而且還是開源,耶!警告: 此功能在 Visual Studio 2017 RC 中尚未實(shí)現(xiàn)。

Throw 表達(dá)式:

圖 11 中的臨時(shí)類可以得到增強(qiáng),在 expression-bodied 成員內(nèi)包括參數(shù)驗(yàn)證;因此,我可以將構(gòu)造函數(shù)更新為:
public TemporaryFile(string fileName) =>  File = new FileInfo(filename ?? throw new ArgumentNullException());如果沒有 throw 表達(dá)式,C# 對(duì) expression-bodied 成員的支持就不能進(jìn)行任何參數(shù)驗(yàn)證。但是,通過 C# 7.0 支持 throw 作為一個(gè)表達(dá)式,而不僅僅是一個(gè)語句,因此,可以在更大的包含表達(dá)式中報(bào)告錯(cuò)誤內(nèi)聯(lián)。警告: 此功能在 Visual Studio 2017 RC 中尚未實(shí)現(xiàn)。

總結(jié)

我承認(rèn),當(dāng)我開始寫這篇文章,以為它會(huì)短得多。然而,由于我花了更多的時(shí)間編程和測試這些功能,因此,我發(fā)現(xiàn)有更多的方式實(shí)現(xiàn) C# 7.0,而不僅僅是通過閱讀功能標(biāo)題和遵照語言開發(fā)。在許多情況下,聲明變量、二進(jìn)制文本、throw表達(dá)式等等,沒有太多地涉及理解和使用功能。但有幾種情況(例如,按引用返回、解構(gòu)函數(shù)和元組)需要比最初所預(yù)期的更多地了解功能。在后一種情況下,不僅要了解語法,還要知道功能何時(shí)是相關(guān)的。C# 7.0 繼續(xù)在快速減少的特性列表(預(yù)先聲明的輸出標(biāo)識(shí)符和缺少 throw 表達(dá)式)中削弱,而與此同時(shí)進(jìn)行擴(kuò)展,以包括對(duì)之前在語言級(jí)別看不到的功能的支持(元組和模式匹配)。希望這個(gè)介紹可以幫助你快速進(jìn)入 C# 7.0 編程領(lǐng)域。有關(guān)本文內(nèi)容之后的 C# 7.0 開發(fā)的更多信息,請(qǐng)查看我在 intellitect.com/csharp7 上的博客,以及我的《Essential C# 7.0》一書的更新(預(yù)計(jì)將在 Visual Studio 2017 投入生產(chǎn)后不久面世)。
Mark Michaelis 是 IntelliTect 的創(chuàng)始人,擔(dān)任首席技術(shù)架構(gòu)師和培訓(xùn)師。在近二十年的時(shí)間里,他一直是 Microsoft MVP,并且自 2007 年以來一直擔(dān)任 Microsoft 區(qū)域總監(jiān)。Michaelis 還是多個(gè) Microsoft 軟件設(shè)計(jì)評(píng)審團(tuán)隊(duì)(包括 C#、Microsoft Azure、SharePoint 和 Visual Studio ALM)的成員。他在開發(fā)者會(huì)議上發(fā)表了演講,并撰寫了大量書籍,包括最新的“必備 C# 6.0(第 5 版)”(itl.tc/EssentialCSharp)。可通過他的 Facebook facebook.com/Mark.Michaelis、博客IntelliTect.com/Mark、Twitter @markmichaelis 或電子郵件 mark@IntelliTect.com 與他取得聯(lián)系。
發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 仪陇县| 香港| 龙门县| 和静县| 莱阳市| 彩票| 惠东县| 眉山市| 察哈| 兰坪| 阜新| 思南县| 永宁县| 辽阳市| 榆社县| 肃北| 邳州市| 青海省| 芒康县| 四会市| 泾阳县| 彰武县| 农安县| 衡南县| 祥云县| 碌曲县| 绥德县| 辽中县| 临江市| 两当县| 年辖:市辖区| 长宁县| 宣汉县| 乌拉特中旗| 东阿县| 库车县| 永德县| 墨竹工卡县| 白银市| 栾城县| 古交市|