Visual Studio 2019 preview中體驗C# 8.0新語法


准備工作:

Visual Studio 2019 Preview版本中並沒有包含所有的C# 8.0的新功能,但目前也有一些可以試用了。在開始之前,需要進行入兩項設置:

  1. 將Framework設置為.net core 3.0
  2. 將C#語法設置為8.0

也可以直接編輯.csproj文件,修改TargetFramework和LangVersion為如下形式:

     <TargetFramework>netcoreapp3.0</TargetFramework>
     <LangVersion>8.0</LangVersion>

 

Nullable reference types

空引用對於所有編程者來說相信都是一個非常頭痛的問題,圖靈獎得主Tony Hoare 就把包含空引用的編程語言用定義為一個十億美元的錯誤Null References: The Billion Dollar Mistake

首先還是來一段簡單的代碼:

     string s = null;
     Console.WriteLine($"The first letter of {s} is {s[0]}");

這段代碼編譯沒有問題,但運行的時候會拋空引用異常的。

在C# 8.0中,開啟了空引用異常檢測后,上述代碼在編譯器就會檢查出告警來。

    

    

並且它會結合上下文判斷,如果該值不會為null,則不會告警,非常智能。

    

細心的朋友可能會發現,雖然在下面使用的地方沒有告警,但是變量初始化的地方還是報告警了。如果我們的程序本身就是允許null值改怎么辦呢,放任告警不管也是不合適的做法。

針對這個問題,C#引入了一個新的聲明為可空對象的語法:

     string? s = null;

也就是在類型后加一個?符號,表面該對象是一個可空對象。

由於這個行為和之前的C#版本是不一致的,因此默認是沒有開啟這個功能的,我們需要在csproj文件中打開這個設置:

     <LangVersion>8.0</LangVersion>
     <NullableReferenceTypes>true</NullableReferenceTypes>

不知道在后續的VS的版本中會不會直接再界面上添加這一設置。

最后總結一下,Nullable reference types主要干了兩件事:

  1. 可以通過對象聲明判斷該對象是否可能為空。
  2. 當可空對象使用在不可空的場景是,會報告警。

雖然之前有一些第三方插件也集成了類似的功能,如Resharper的Null Check,但把這個功能集成到了編譯器上后更加簡潔好用。

C#的空對象檢查在設計期間也有好幾種語法方案,目前這種方案既解決了問題,又對現有代碼保持完全兼容,還能對現有代碼潛在性問題能進行分析,是一種比較理想的方案的。如果以后能通過設置,將空引用的告警級別可以設置為錯誤就更好了。

 

Ranges and indices

范圍和索引是C#新引入的語法,它主要引入了兩個對象Range和Index。

Index

首先還是來看一個簡單的例子。

     var numbers = new[] { 1, 2, 3, 4, 5, 6, 7 };
     Index i1 = 3; // number 3 from beginning
     Index i2 = ^2; // number 2 from end
     Console.WriteLine($"{numbers[i1]}, {numbers[i2]}"); // "4, 6"

這個例子簡單的演示了一下Index的用法,Index本身還是類似於之前的int索引的,它也可以和int類型轉換。但Index在int索引的方式擴展了一下,支持從后往前訪問,也就是我們說的倒數位。

     Index i2 = ^2; // number 2 from end

Range

基於Index組成起點和終點,可以組成了一個范圍Range,根據Range可以對數組進行切片。

     Range range = Range.Create(i1, i2);
     int[] slice = numbers[range];        //"4, 5"

".."運算符

為了快速表示一個Range,C#還映入了一個新的運算符".."如上面的代碼就可以簡寫為:

     int[] slice = numbers[i1..i2];        //"4, 5"

".."語法不復雜,通過".."連接的開頭和結尾的索引,用來表示一個范圍。為了使用方便,".."運算符的開頭和結尾是可以省略的,常用的大致就有這幾種形式。

     string text = "hello c# 8.0";
     Console.WriteLine(text[..]); //"hello c# 8.0"
     Console.WriteLine(text[^3..]);      //"8.0"
     Console.WriteLine(text[..5]);       //"hello"
     Console.WriteLine(text[6..]);       //"c# 8.0"

通過".."運算符,我們描述切片時可以清晰很多,例如如下這個常見的求字符串子串的例子:

     var sub = text.Substring(text.Length - 6, 6);
     var sub2 = text[^6..];

.net 3.0的很多類都內置了對Range的切片操作,常見的有:

  1. 字符串用來子串
  2. Array用來划獲取子數組
  3. span<T>用來切片

 

Asynchronous streams

異步流能一種拉的方式進行異步迭代,配合async編程可以以異步的方式把socket流像本地文件一樣解析,相信這是很多用c#寫socket程序的程序員所喜歡的一個特性。

一個簡單的示例如下:

     static async IAsyncEnumerable<string> GetNamesAsync()
     {
        await Task.Delay(1000);
        yield return "hello";
        await Task.Delay(1000);
        yield return "world";
     }
    

     await foreach (var name in GetNamesAsync())
     {
        Console.WriteLine(name);
     }

我在Visual Studio 2019 preview中試用這個功能的時候,發現無法編譯通過。MS解釋說這個是VS和.net core代碼沒有完全匹配上所致,我們可以手動添加相關代碼以完成這一編譯過程。 

namespace System.Threading.Tasks
{
    using System.Runtime.CompilerServices;
    using global::System.Threading.Tasks.Sources;

    internal struct ManualResetValueTaskSourceLogic<TResult>
    {
        private ManualResetValueTaskSourceCore<TResult> _core;
        public ManualResetValueTaskSourceLogic(IStrongBox<ManualResetValueTaskSourceLogic<TResult>> parent) : this() { }
        public short Version => _core.Version;
        public TResult GetResult(short token) => _core.GetResult(token);
        public ValueTaskSourceStatus GetStatus(short token) => _core.GetStatus(token);
        public void OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags) => _core.OnCompleted(continuation, state, token, flags);
        public void Reset() => _core.Reset();
        public void SetResult(TResult result) => _core.SetResult(result);
        public void SetException(Exception error) => _core.SetException(error);
    }
}

namespace System.Runtime.CompilerServices
{
    internal interface IStrongBox<T> { ref T Value { get; } }
}
View Code

 

其他語法

本身C# 8.0是還有幾個其它語法的,如接口默認方法,高級模式匹配等。這些語法在目前的VS 2019 preview中還無法體驗。估計后續會慢慢放開的,到時候我再寫相關文章介紹它們。

相關文章:

https://blogs.msdn.microsoft.com/dotnet/2018/12/05/take-c-8-0-for-a-spin/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM