準備工做: html
Visual Studio 2019 Preview版本中並無包含全部的C# 8.0的新功能,但目前也有一些能夠試用了。在開始以前,須要進行入兩項設置:git
也能夠直接編輯.csproj文件,修改TargetFramework和LangVersion爲以下形式:程序員
<TargetFramework>netcoreapp3.0</TargetFramework>
<LangVersion>8.0</LangVersion> github
Nullable reference types 編程
空引用對於全部編程者來講相信都是一個很是頭痛的問題,圖靈獎得主Tony Hoare 就把包含空引用的編程語言用定義爲一個十億美圓的錯誤Null References: The Billion Dollar Mistake。 c#
首先仍是來一段簡單的代碼: 數組
string s = null;
Console.WriteLine($"The first letter of {s} is {s[0]}"); app
這段代碼編譯沒有問題,但運行的時候會拋空引用異常的。 異步
在C# 8.0中,開啓了空引用異常檢測後,上述代碼在編譯器就會檢查出告警來。 socket
而且它會結合上下文判斷,若是該值不會爲null,則不會告警,很是智能。
細心的朋友可能會發現,雖然在下面使用的地方沒有告警,可是變量初始化的地方仍是報告警了。若是咱們的程序自己就是容許null值改怎麼辦呢,聽任告警無論也是不合適的作法。
針對這個問題,C#引入了一個新的聲明爲可空對象的語法:
string? s = null;
也就是在類型後加一個?符號,表面該對象是一個可空對象。
因爲這個行爲和以前的C#版本是不一致的,所以默認是沒有開啓這個功能的,咱們須要在csproj文件中打開這個設置:
<LangVersion>8.0</LangVersion>
<NullableReferenceTypes>true</NullableReferenceTypes>
不知道在後續的VS的版本中會不會直接再界面上添加這一設置。
最後總結一下,Nullable reference types主要乾了兩件事:
雖然以前有一些第三方插件也集成了相似的功能,如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的切片操做,常見的有:
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; } } }
其餘語法
自己C# 8.0是還有幾個其它語法的,如接口默認方法,高級模式匹配等。這些語法在目前的VS 2019 preview中還沒法體驗。估計後續會慢慢放開的,到時候我再寫相關文章介紹它們。
相關文章:
https://blogs.msdn.microsoft.com/dotnet/2018/12/05/take-c-8-0-for-a-spin/