久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

最新和改進的 C# 6.0

 蘭亭文藝 2019-04-26

最新和改進的 C# 6.0

Mark Michaelis

盡管 C# 6.0 尚未完成,,但現(xiàn)在這些功能正處于接近完成的關(guān)鍵時刻,。自 2014 年 5 月發(fā)布文章“C# 6.0 語言預(yù)覽版”(msdn.microsoft.com/magazine/dn683793.aspx) 以來,下一版本的 Visual Studio 的 CTP3 版本中對 C# 6.0 進行了一些變更和改進(代號為“14”),。

本文中,,我將介紹各種新功能,并提供針對五月所討論的功能的更新,。我還會保留對 C# 6. 每個功能的全面,、最新的博客介紹更新。請訪問 /csharp6,。其中的很多示例均來自我的“Essential C# 6.0”(Addison-Wesley Professional) 一書的下一版,。

Null 條件運算符

即使是 .NET 開發(fā)新手,也可能非常熟悉 NullReferenceException。有一個例外是幾乎總是會指出一個 Bug,,因為開發(fā)人員在調(diào)用 (null) 對象的成員之前未進行充分的 null 檢查,。請看看以下示例:

public static string Truncate(string value, int length){  string result = value;  if (value != null) // Skip empty string check for elucidation  {    result = value.Substring(0, Math.Min(value.Length, length));  }  return result;}

如果不進行 null 檢查,,此方法會引發(fā) NullReferenceException,。盡管這很簡單,但檢查字符串參數(shù)是否為 null 的過程卻稍微有些繁瑣,。通常,,考慮到比較的頻率,該繁瑣的方法可能沒有必要,。C# 6.0 包括一個新的 null 條件運算符,,可幫助您更加簡便地編寫這些檢查:

public static string Truncate(string value, int length){            return value?.Substring(0, Math.Min(value.Length, length));}[TestMethod]public void Truncate_WithNull_ReturnsNull(){  Assert.AreEqual<string>(null, Truncate(null, 42));}

根據(jù) Truncate_WithNull_ReturnsNull 方法所演示的內(nèi)容,,如果對象的值實際上為 null,,則 null 條件運算符將返回 null。這帶來了一個問題,,即 null 條件運算符在調(diào)用鏈中出現(xiàn)時會是什么情況,?如以下示例中所示:

public static string AdjustWidth(string value, int length){  return value?.Substring(0, Math.Min(value.Length, length)).PadRight(length);}[TestMethod]public void AdjustWidth_GivenInigoMontoya42_ReturnsInigoMontoyaExtended(){  Assert.AreEqual<int>(42, AdjustWidth('Inigo Montoya', 42).Length);}

盡管 Substring 是通過 null 條件運算符進行調(diào)用的,,并且 null value?.Substring 似乎返回了 null,,但語言行為按您的想法進行。這簡化了對 PadRight 的調(diào)用過程,,并立即返回 null,,從而避免會導(dǎo)致出現(xiàn) NullReferenceException 的編程錯誤。這個概念稱為“null 傳播”,。

Null 條件運算符會根據(jù)具體條件進行 null 檢查,,然后再調(diào)用目標方法以及調(diào)用鏈中的所有其他方法。這將可能產(chǎn)生一個令人驚訝的結(jié)果,,例如,,text?.Length.GetType 語句中的結(jié)果。

如果 null 條件運算符在調(diào)用目標為 null 時返回 null,,那么調(diào)用會返回值類型的成員時最終會是什么數(shù)據(jù)類型(假定值類型不能為 null),?例如,從 value?.Length 返回的數(shù)據(jù)類型不能只是 int,。答案當然是:可以為 null 的類型(int,?)。實際上,,嘗試僅將結(jié)果分配給 int 將會出現(xiàn)編譯錯誤:

int length = text?.Length; // Compile Error: Cannot implicitly convert type 'int?' to 'int'

Null 條件具有兩種語法形式,。首先,,問號在點運算符前面 (?.),。其次,將問號和索引運算符結(jié)合使用,。例如,,給定一個集合(而非在索引到集合之前顯式進行 null 檢查),您就可以使用 null 條件運算符執(zhí)行此操作:

public static IEnumerable<T> GetValueTypeItems<T>(  IList<T> collection, params int[] indexes)  where T : struct{  foreach (int index in indexes)  {    T? item = collection?[index];    if (item != null) yield return (T)item;  }}

此示例使用了運算符 ?[…] 的 null 條件索引形式,,導(dǎo)致僅在集合不為 null 時才索引到集合。通過 null 條件運算符的此形式,,T? item = collection?[index] 語句在行為上相當于:

T? item = (collection != null) ? collection[index] : null.

請注意,null 條件運算符僅可檢索項目,,不會分配項目,。如果給定 null 集合,那么這意味著什么,?

請注意針對引用類型使用 ?[…] 時的隱式歧義,。由于引用類型可以為 null,因此對于集合是否為 null,,或者是否元素本身實際上就是 null 而言,,來自 ?[…] 運算符的 null 結(jié)果不明確。

Null 條件運算符的一個非常有用的應(yīng)用程序解決了 C# 自 C# 1.0 以來一直存在的的一個特性,,即在調(diào)用委托之前檢查是否為 null,。我們來看一下圖 1 中顯示的 C# 2.0 代碼。

圖 1 在調(diào)用委托之前檢查是否為 Null

class Theremostat{  event EventHandler<float> OnTemperatureChanged;  private int _Temperature;  public int Temperature  {    get    {      return _Temperature;    }    set    {      // If there are any subscribers, then      // notify them of changes in temperature      EventHandler<float> localOnChanged =        OnTemperatureChanged;      if (localOnChanged != null)      {        _Temperature = value;        // Call subscribers        localOnChanged(this, value);      }    }  }}

通過使用 null 條件運算符,,整個 set 實現(xiàn)過程就可簡化為:

OnTemperatureChanged?.Invoke(this, value)

現(xiàn)在,,您只需對將 null 條件運算符作為前綴的 Invoke 進行調(diào)用,,不再需要將委托實例分配給本地變量,從而實現(xiàn)線程安全,,甚至是在調(diào)用委托之前顯式檢查值是否為 null,。

C# 開發(fā)人員都很想知道在最新的四個版本中是否對此內(nèi)容有所改進。答案是最終進行了改進,。僅此一項功能就可以改變調(diào)用委托的方式,。

另一個 null 條件運算符普及的常見模式是與 coalesce 運算符結(jié)合使用。您無需在調(diào)用 Length 之前對 linesOfCode 進行 null 檢查,,而是可以編寫項目計數(shù)算法,,如下所示:

List<string> linesOfCode = ParseSourceCodeFile('Program.cs');return linesOfCode?.Count ?? 0;

在這種情況下,任何空集合(無項目)和 null 集合均標準化為返回相同數(shù)量,??傊琻ull 條件運算符將實現(xiàn)以下功能:

  • 如果操作數(shù)為 null,,則返回 null

  • 如果操作數(shù)為 null,,則簡化調(diào)用鏈中的其他調(diào)用

  • 如果目標成員返回一個值類型,則返回可以為 null 的類型 (System.Nullable<T>),。

  • 以線程安全的方式支持委托調(diào)用

  • 可用作成員運算符 (?.) 和索引運算符 (?[…])

自動屬性初始化表達式

有過正確實現(xiàn)結(jié)構(gòu)經(jīng)驗的所有 .NET 開發(fā)人員無疑都為一個問題所困擾:需要使用多少語法才能使類型固定不變(為 .NET 標準建議的類型),。此問題實際上是只讀屬性存在的問題:

  1. 定義為只讀的支持字段

  2. 構(gòu)造函數(shù)內(nèi)支持字段的初始化

  3. 屬性的顯式實現(xiàn)(而非使用自動屬性)

  4. 返回支持字段的顯式 getter 實現(xiàn)

所有這一切僅僅是為了“正確地”實現(xiàn)固定不變的屬性。之后,,此情況還會針對類型的所有屬性重復(fù)發(fā)生,。因此,正確操作需要比不堪一擊的方法付出明顯更多的努力,。發(fā)布了自動屬性初始化表達式(CTP3 還包括對初始化表達式的支持)這個新功能后,,C# 6.0 就可派上用場了。自動屬性初始化表達式允許直接在屬性的聲明內(nèi)分配屬性,。對于只讀屬性,,它負責確保屬性固定不變所需的所有繁瑣程序。例如,,請看本示例中的 FingerPrint 類:

public class FingerPrint{  public DateTime TimeStamp { get; } = DateTime.UtcNow;  public string User { get; } =    System.Security.Principal.WindowsPrincipal.Current.Identity.Name;  public string Process { get; } =    System.Diagnostics.Process.GetCurrentProcess().ProcessName;}

如代碼所示,屬性初始化表達式允許向?qū)傩苑峙湟粋€初始值作為屬性聲明的一部分,。屬性可以是只讀的(只包含 getter),,也可以是讀/寫(包含 setter 和 getter)的。如果是只讀的,,則基礎(chǔ)支持字段將通過只讀修飾符自動聲明,。這就確保了在初始化之后會固定不變。

初始化表達式可以是任意表達式,。例如,,通過使用條件運算符,您可以設(shè)置默認初始化值:

public string Config { get; } = string.IsNullOrWhiteSpace(  string connectionString =    (string)Properties.Settings.Default.Context?['connectionString'])?  connectionString : '<none>';

本示例中,,請注意之前的文章中所討論的如何使用聲明表達式(請參閱 /?p=4040)。如果您需要的不只是表達式,,可以將初始化重構(gòu)到靜態(tài)方法中,,然后對其進行調(diào)用。

Nameof 表達式

CTP3 版本中介紹的另一個新增功能是支持 nameof 表達式,。您將多次需要在代碼中使用“魔幻字符串”,。此類“魔幻字符串”是映射到您代碼中的程序元素的普通 C# 字符串,。例如,引發(fā) ArgumentNullException 時,,使用一個字符串表示無效對應(yīng)參數(shù)的名稱,。遺憾的是,這些魔幻字符串未經(jīng)過編譯時驗證,,任意程序元素更改(例如,,重命名參數(shù))都不會自動更新魔幻字符串,,從而導(dǎo)致不一致,,而編譯器根本不會發(fā)現(xiàn)此問題。

在其他情況下,,例如引發(fā) OnPropertyChanged 事件時,,可以通過提取名稱的樹表達式技術(shù)避免出現(xiàn)魔幻字符串。鑒于操作簡單(只識別程序元素名稱),,所以這或許有點讓人頭疼,。無論哪種情況,解決方案都不太理想,。

若要解決這一特性,,C# 6.0 提供了對“程序元素”名稱的訪問權(quán)限,無論是類名稱,、方法名稱,、參數(shù)名稱還是特定屬性名稱(可能是對于使用反射的情況)。例如,,圖 2 中的代碼使用 nameof 表達式提取參數(shù)的名稱,。

圖 2 使用 Nameof 表達式提取參數(shù)名稱

void ThrowArgumentNullExceptionUsingNameOf(string param1){  throw new ArgumentNullException(nameof(param1));}[TestMethod]public void NameOf_UsingNameofExpressionInArgumentNullException(){  try  {    ThrowArgumentNullExceptionUsingNameOf('data');    Assert.Fail('This code should not be reached');  }  catch (ArgumentNullException exception)  {    Assert.AreEqual<string>('param1', exception.ParamName);}

正如測試方法所演示的,,ArgumentNullException 的 ParamName 屬性具有 param1 值,,這是使用方法中的 nameof(param1) 表達式的值集。Nameof 表達式不僅僅用于參數(shù),,您還可以使用它來檢索所有編程元素,,如圖 3 中所示。

圖 3 檢索其他編程元素

namespace CSharp6.Tests{  [TestClass]  public class NameofTests  {    [TestMethod]    public void Nameof_ExtractsName()    {      Assert.AreEqual<string>('NameofTests', nameof(NameofTests));      Assert.AreEqual<string>('TestMethodAttribute',        nameof(TestMethodAttribute));      Assert.AreEqual<string>('TestMethodAttribute',        nameof(         Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute));      Assert.AreEqual<string>('Nameof_ExtractsName',        string.Format('{0}', nameof(Nameof_ExtractsName)));      Assert.AreEqual<string>('Nameof_ExtractsName',        string.Format('{0}', nameof(        CSharp6.Tests.NameofTests.Nameof_ExtractsName)));    }  }}

Nameof 表達式僅檢索最終的標識符,,即使您使用更多的顯式包含點的名稱也是如此。此外,,對于屬性而言,,未隱含“Attribute”后綴。相反,,編譯需要它,。它非常適合用于清理混亂代碼,。

主構(gòu)造函數(shù)

自動屬性初始化表達式尤其適合與主構(gòu)造函數(shù)結(jié)合使用。主構(gòu)造函數(shù)為降低常見對象模式的繁瑣程度提供了一種方法,。此功能自五月以來已顯著改進,。更新包括:

  1. 主構(gòu)造函數(shù)的可選實現(xiàn)主體:這將支持此前不受支持的主構(gòu)造函數(shù)參數(shù)驗證和初始化等。

  2. 取消字段參數(shù):通過主構(gòu)造函數(shù)參數(shù)對字段進行聲明,。(不將此功能按照已定義方式推出是正確的決定,,因為它不再按照 C# 之前矛盾的方式強制遵循特定命名約定。)

  3. 支持表達式主體函數(shù)和屬性(本文稍后將進行討論),。

隨著 Web 服務(wù),、多層應(yīng)用程序、數(shù)據(jù)服務(wù),、Web API,、JSON 及類似技術(shù)的普遍使用,類的一個普遍形式是數(shù)據(jù)傳輸對象 (DTO),。DTO 通常不會實現(xiàn)太多功能,,而是專注于使數(shù)據(jù)存儲簡單化。它對于簡單性的關(guān)注使得主構(gòu)造函數(shù)極具新引力,。例如,,請看本示例中所示的固定 Pair 數(shù)據(jù)結(jié)構(gòu):

struct Pair<T>(T first, T second){  public T First { get; } = first;  public T Second { get; } = second;  // Equality operator ...}

構(gòu)造函數(shù)定義 Pair(string first, string second) 已合并到類聲明中,。這會將構(gòu)造函數(shù)參數(shù)指定為 first 和 second(均為類型 T),。屬性初始化表達式中也引用了這些參數(shù),并將參數(shù)分配給其對應(yīng)的屬性,。當您發(fā)現(xiàn)此類定義的簡單性,、對不變性的支持以及必不可少的構(gòu)造函數(shù)(所有屬性/字段的初始化表達式)時,您將會了解到它是如何幫助您正確編寫代碼的,。這將導(dǎo)致先前需要不必要的詳細級別的常見模式得到顯著改進,。

主構(gòu)造函數(shù)主體指定對主構(gòu)造函數(shù)執(zhí)行的操作。這將有助于您在主構(gòu)造函數(shù)上實現(xiàn)通常在構(gòu)造函數(shù)上可以實現(xiàn)的等同功能,。例如,,改進 Pair<T> 數(shù)據(jù)結(jié)構(gòu)的可靠性的下一個步驟可能是提供屬性驗證。此類驗證可以確保 Pair.First 的 null 值將無效?,F(xiàn)在,,CTP3 包括一個主構(gòu)造函數(shù)主體(未聲明的構(gòu)造函數(shù)主體),如圖 4 中所示,。

圖 4 實現(xiàn)主構(gòu)造函數(shù)主體

struct Pair<T>(T first, T second){  {    if (first == null) throw new ArgumentNullException('first');    First = first; // NOTE: Not working in CTP3  }       public T First { get; }; // NOTE: Results in compile error for CTP3  public T Second { get; } = second;  public int CompareTo(T first, T second)  {    return first.CompareTo(First) + second.CompareTo(Second);  }// Equality operator ...}

為清晰起見,我將主構(gòu)造函數(shù)主體置于類的第一個成員中,。但這不是 C# 所要求的,。主構(gòu)造函數(shù)主體可以按與其他類成員相關(guān)的任意順序顯示,。

只讀屬性的另一個功能盡管在 CTP3 中沒有發(fā)揮作用,但您可以從構(gòu)造函數(shù)內(nèi)直接分配這些屬性(例如,,F(xiàn)irst = first),。這不僅僅限于主構(gòu)造函數(shù),而且還適用于所有構(gòu)造函數(shù)成員,。

支持自動屬性初始化表達式的一個有趣的結(jié)果是,,它解決了早期版本中出現(xiàn)的需要顯式字段聲明的多種情況。它沒有解決一個最明顯的問題,,即需要對 setter 進行驗證的情況,。另一方面,幾乎已不需要聲明只讀字段?,F(xiàn)在,,無論何時聲明只讀字段,只要需要該封裝級別,,您都可以將只讀自動屬性聲明為私有。

CompareTo 方法具有參數(shù) first 和 second,,這好像與主構(gòu)造函數(shù)的參數(shù)名稱重復(fù),。由于主構(gòu)造函數(shù)名稱在自動屬性初始化表達式作用域內(nèi),因此,,first 和 second 似乎并不明確,。幸運的是,實際情況并非如此,。作用域規(guī)則將依據(jù)不同維度而定,,而您之前在 C# 中并未看到。

在 C# 6.0 之前,,作用域始終由代碼內(nèi)的變量聲明放置來確定,。參數(shù)在其幫助聲明的方法中綁定,字段在類中綁定,,在 if 語句中聲明的變量由 if 語句條件主體綁定,。

相比之下,主構(gòu)造函數(shù)參數(shù)則由時間來綁定,。主構(gòu)造函數(shù)參數(shù)僅在執(zhí)行主構(gòu)造函數(shù)時為“活動”狀態(tài),。此時間范圍在主構(gòu)造函數(shù)主體的情況中很明顯??赡軐τ谧詣訉傩猿跏蓟磉_式的情況不太明顯,。

但是,與作為 C# 1.0+ 中的類初始化表達式的一部分執(zhí)行的轉(zhuǎn)換為語句的字段初始化表達式類似,,自動屬性初始化表達式也通過同樣的方式實現(xiàn),。換言之,,主構(gòu)造函數(shù)參數(shù)的作用域與類初始化表達式和主構(gòu)造函數(shù)主體的生命周期綁定。在自動屬性初始化表達式或主構(gòu)造函數(shù)主體外部對主構(gòu)造函數(shù)參數(shù)進行任何引用都將產(chǎn)生編譯錯誤,。

還有其他幾個與主構(gòu)造函數(shù)相關(guān)的概念需要牢記,。只有主構(gòu)造函數(shù)可以調(diào)用基構(gòu)造函數(shù)。您可以使用主構(gòu)造函數(shù)聲明后跟的基本(上下文)關(guān)鍵字執(zhí)行此操作:

class UsbConnectionException(  string message, Exception innerException, HidDeviceInfo hidDeviceInfo):    Exception  (message, innerException){  public HidDeviceInfo HidDeviceInfo { get;  } = hidDeviceInfo;}

如果指定其他構(gòu)造函數(shù),,則構(gòu)造函數(shù)調(diào)用鏈必須最后調(diào)用主構(gòu)造函數(shù)。這意味著主構(gòu)造函數(shù)不能具有此初始化表達式,。假定主構(gòu)造函數(shù)也不是默認構(gòu)造函數(shù),,所有其他構(gòu)造函數(shù)必須具有這些初始化表達式:

public class Patent(string title, string yearOfPublication){  public Patent(string title, string yearOfPublication,    IEnumerable<string> inventors)    ...this(title, yearOfPublication)  {    Inventors.AddRange(inventors);  }}

希望這些示例有助于展示主構(gòu)造函數(shù)簡化了 C#,。通過主構(gòu)造函數(shù),,還有機會以簡單的方式來執(zhí)行簡單的任務(wù),而不是以復(fù)雜的方式來執(zhí)行簡單的任務(wù),。它偶爾也會讓類包含多個主構(gòu)造函數(shù)和調(diào)用鏈,,致使代碼不易于閱讀。如果您遇到主構(gòu)造函數(shù)語法使代碼看起來更為復(fù)雜而不是簡化代碼的情況,,那么請不要使用主構(gòu)造函數(shù),。對于 C# 6.0 的所有增強功能,如果您有不喜歡的功能,,或某個功能使您的代碼不易于閱讀,,請不要使用該功能。

表達式主體函數(shù)和表達式屬性

表達式主體函數(shù)是 C# 6.0 中的另一個語法精簡形式,。有一些函數(shù)不包括語句體,,而是以函數(shù)聲明后跟表達式的形式來實現(xiàn)。

例如,,可以這樣向 Pair<T> 類添加 ToString 方法的重寫:

public override string ToString() => string.Format('{0}, {1}', First, Second);

表達式主體函數(shù)沒有什么徹底更改。和 C# 6.0 中的大部分功能一樣,,它們旨在提供簡化的語法,,用于實現(xiàn)簡單的情況。當然,,表達式的返回類型必須與函數(shù)聲明中定義的返回類型相匹配,。在這種情況下,ToString 將返回一個字符串,,這同函數(shù)實現(xiàn)表達式返回的結(jié)果一樣,。返回 void 或 Task 的方法應(yīng)通過同樣不會返回任何結(jié)果的表達式實現(xiàn)。

表達式主體簡化不僅僅限于函數(shù),您還可以使用表達式實現(xiàn)只讀(僅包含 getter)屬性——表達式屬性,。例如,,可以將 Text 成員添加到 FingerPrint 類:

public string Text =>  string.Format('{0}: {1} - {2} ({3})', TimeStamp, Process, Config, User);

其他功能

有一些功能不再計劃針對 C# 6.0 實現(xiàn):

  • 索引屬性運算符 ($) 不再可用,,并且不針對 C# 6.0 實現(xiàn),。

  • 盡管索引成員語法預(yù)期在 C# 6.0 的更高版本中回歸,但它在 CTP3 中不起作用:

var cppHelloWorldProgram = new Dictionary<int, string>{[10] = 'main() {',[20] = '    printf(\'hello, world\')',[30] = '}'};
  • 主構(gòu)造函數(shù)中的字段參數(shù)不再屬于 C# 6.0,。

  • 二進制數(shù)字文本和數(shù)字文本中的數(shù)字分隔符 ('_’) 在正式發(fā)布之前尚不確定。

有一些功能未在本文中進行討論,,因為這些功能已涵蓋在五月的文章中,,但靜態(tài) using 語句(請參閱 /?p=4038)、聲明表達式(請參閱 /?p=4040,,以及異常處理改進(請參閱 /?p=4042)這些功能保持不變,。

總結(jié)

顯然,開發(fā)人員對 C# 興趣盎然,,希望確保其一如既往地保持卓越的功能,。語言團隊會非常認真地對待您的反饋,并根據(jù)用戶反饋對語言進行適當修改,。歡迎訪問 roslyn.,,并將您的想法告知團隊。此外,,請記得查看 /csharp6 來了解 C# 6.0 發(fā)布后的更新。


Mark Michaelis 是 IntelliTect 的創(chuàng)始人,。他還擔任首席技術(shù)架構(gòu)師和培訓(xùn)師,。自 1996 年以來,他就成為 Microsoft C#,、Visual Studio Team System (VSTS) 和 Windows SDK 方面的 MVP,。他在 2007 年就任 Microsoft 區(qū)域總監(jiān)。另外,,他還是多個 Microsoft 軟件設(shè)計評審團隊(包括 C#,、連通系統(tǒng)部門和 VSTS)的成員。Michaelis 在開發(fā)人員大會上發(fā)表講話,,還撰寫了許多文章和書籍,,目前正在撰寫新一版的《Essential C#》(C# 本質(zhì)論)(Addison-Wesley Professional)。

衷心感謝以下 Microsoft 技術(shù)專家對本文的審閱:Mads Torgersen

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,,謹防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報,。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多