1. 從 C# 1.0 到 C# 9.0語言特性匯總一覽!

        共 8573字,需瀏覽 18分鐘

         ·

        2021-02-21 15:16


        轉(zhuǎn)自:雪輕鴻
        qinyuanpei.blog.csdn.net/article/details/113720157

        一、C# 版本歷史記錄圖片




        說明:因?yàn)镸arkdown下維護(hù)這樣復(fù)雜的表格有一點(diǎn)麻煩,故,這里以圖片形式展示出來,如后續(xù)內(nèi)容有更新,請?jiān)L問原始筆記鏈接。為知筆記的表格渲染在移動(dòng)端表現(xiàn)不佳,為了獲得更好的閱讀體驗(yàn),請?jiān)陔娔X端訪問查看。


        二、C# 版本特性說明


        現(xiàn)在是 2021 年,相信 C# 7.0 以前的版本大家都應(yīng)該沒有什么問題,因?yàn)橄癫┲鬟@樣的 90 后“中年”男人,接觸的都是這個(gè)版本的 C#。所以,在這里我們主要講解大家C# 7.0、8.0 以及 9.0 的語法特性。考慮到文章篇幅有限,這里選取的都是博主個(gè)人比較喜歡的語法特性,如果這里沒有你喜歡的特性,請參考文章末尾的參考鏈接。如果這里的特性你都不喜歡,請你馬上關(guān)掉這個(gè)網(wǎng)頁,愿這個(gè)世界:Love & Peace??赡苣銜?huì)感覺到我說話變得小心翼翼起來,因?yàn)檫@個(gè)世界上有種叫做“杠精”的生物,當(dāng)它從我的只言片語里讀出那些挫敗感的時(shí)候,終于有了嘲笑我們這批步入30歲行列的90后的底氣,沒錯(cuò),我在最近的博客評論中被讀者“嘲諷”了,讓暴風(fēng)雨來得更猛烈一些吧!


        三、C# 7.0


        在 C# 7.0 中,我個(gè)人比較喜歡的特性主要有以下幾個(gè):元組和棄元、更多的 expression-bodied 成員、out 變量、異步 Main 方法、模式匹配 和 引發(fā)表達(dá)式。


        元組和棄元


        這個(gè)概念乍聽起來可能會(huì)有一點(diǎn)陌生,其實(shí),按我的理解,這就是增強(qiáng)的元組語法,終于可以擺脫Item1、Item2…啦:


        //示例1
        (string Alpha, string Beta) namedLetters = ("a", "b");
        Console.WriteLine($"{namedLetters.Alpha}, {namedLetters.Beta}");

        //示例2
        var alphabetStart = (Alpha: "a", Beta: "b");
        Console.WriteLine($"{alphabetStart.Alpha}, {alphabetStart.Beta}");

        //示例3
        int count = 5;
        string label = "Colors used in the map";
        var pair = (count, label);
        Console.WriteLine(pair);


        有一段時(shí)間,前端同事總和我吹噓 ES6 里面的解構(gòu)多么多么好用!對此,我想說,C# 一樣可以解構(gòu),假設(shè)我們現(xiàn)在有下面的一個(gè)方法:


        static (string, double, double) GetLocation() 
        {
        var city = "西安市";
        var lat = 33.42d;
        var lon = 107.40d;
        return (city, lon, lat);
        }


        這就是簡化后的元組的用法,如果是以前,我們還需要返回一個(gè)Tuple。此時(shí),如果我們需要解析城市名稱及其經(jīng)緯度,可以這樣做:


        //示例4
        (string city, double lon, double lat) = GetLocation();
        Console.WriteLine($"{city},({lon},{lat})");


        OK,那么什么又是棄元呢?繼續(xù)以上面的代碼為例,如果我不關(guān)心經(jīng)緯度,只需要城市名稱又該怎么辦呢?人家的方法返回的是一個(gè)3元的結(jié)果,而我們只需要其中的1元,此時(shí),就有了所謂棄元的概念:


        (string city, _, _) = GetLocation();
        Console.WriteLine($"{city}");


        在 C# 中可以使用下劃線_來表示要舍棄的元,是為棄元,怎么樣?你學(xué)會(huì)了嗎?


        更多的 expression-bodied 成員


        這部分同樣是經(jīng)過強(qiáng)化的 Lambda 表達(dá)式,之前我們可以在成員函數(shù)和 只讀屬性上使用 Lambda 表達(dá)式,而現(xiàn)在,我們可以將其運(yùn)用在構(gòu)造函數(shù)、終結(jié)器以及 get和set訪問器:


        // Expression-bodied constructor
        public ExpressionMembersExample(string label) => this.Label = label;

        // Expression-bodied finalizer
        ~ExpressionMembersExample() => Console.Error.WriteLine("Finalized!");

        private string label;

        // Expression-bodied get / set accessors.
        public string Label
        {
        get => label;
        set => this.label = value ?? "Default label";
        }


        out變量


        個(gè)人認(rèn)為,這是一個(gè)非常不錯(cuò)的改進(jìn),終于不用再單獨(dú)聲明out變量啦:


        if (int.TryParse(input, out int result))
        Console.WriteLine(result);
        else
        Console.WriteLine("Could not parse input");


        異步 Main 方法


        顧名思義,Main 方法現(xiàn)在可以支持 async 關(guān)鍵字啦:


        static async Task<int> Main()
        {
        // This could also be replaced with the body
        // DoAsyncWork, including its await expressions:
        return await DoAsyncWork();
        }


        在沒有返回值的情況下,可以考慮返回Task:


        static async Task Main()
        {
        await SomeAsyncMethod();
        }


        模式匹配


        主要是針對 is 和 switch 語句提供了增強(qiáng)的語法。在這里,對于前者來說,我們可以將判斷和賦值兩個(gè)步驟合二為一:


        public static double ComputeAreaModernIs(object shape)
        {
        if (shape is Square s)
        return s.Side * s.Side;
        else if (shape is Circle c)
        return c.Radius * c.Radius * Math.PI
        ;
        else if (shape is Rectangle r)
        return r.Height * r.Length
        ;
        // elided
        throw new ArgumentException(
        message: "shape is not a recognized shape",
        paramName: nameof(shape));
        }


        而對于后者來說,主要打破了傳統(tǒng) switch 語句的常量模式:


        public static double ComputeArea_Version3(object shape)
        {
        switch (shape)
        {
        case Square s when s.Side == 0:
        case Circle c when c.Radius == 0:
        return 0;
        case Square s:
        return s.Side * s.Side;
        case Circle c:
        return c.Radius * c.Radius * Math.PI;
        default:
        throw new ArgumentException(
        message: "shape is not a recognized shape",
        paramName: nameof(shape));
        }
        }


        引發(fā)表達(dá)式


        這個(gè)主要是針對 throw 關(guān)鍵字的增強(qiáng),當(dāng)我看到微軟的文檔的時(shí)候,我突然意識到,這個(gè)語法其實(shí)我用了很久啦!


        //場景A:條件運(yùn)算符
        string arg = args.Length >= 1 ? args[0] :
        throw new ArgumentException("You must supply an argument");
        //場景B:Null合并運(yùn)算符
        public string Name
        {
        get => name;
        set => name = value ??
        throw new ArgumentNullException(
        paramName: nameof(value),
        message: "Name cannot be null");
        }
        //場景C:Lambda表達(dá)式
        DateTime ToDateTime(IFormatProvider provider) =>
        throw new InvalidCastException("Conversion to a DateTime is not supported.");


        以上,就是 C# 7.0 中我個(gè)人比較喜歡的語法特性。需要了解所有 C# 7.0 語法特性的小伙伴們,則可以參考這里:C# 7.0 - C# 7.3 中的新增功能。


        四、C# 8.0


        在 C# 8.0 中,我個(gè)人比較喜歡的特性主要有以下幾個(gè):默認(rèn)接口方法、異步流、索引和范圍。


        默認(rèn)接口方法


        關(guān)于這個(gè),我覺得有點(diǎn)多此一舉,如果一定要有一個(gè)默認(rèn)行為,那你用繼承來實(shí)現(xiàn)不就好啦,接口本來就是用來實(shí)現(xiàn)的啊摔!


        public class ChineseSayHello : ISayHello
        {
        public string Who { get; set; }
        }
        public interface ISayHello
        {
        private const string DefaultPersopn = "Anumouse";
        string Who { get; set; }
        void SayHello()
        {
        Who = DefaultPersopn;
        Console.WriteLine($"Hello, {Who}");
        }
        }


        在上面這個(gè)例子里,ChineseSayHello沒有實(shí)現(xiàn)SayHello()方法不影響編譯,因?yàn)镮SayHello有默認(rèn)實(shí)現(xiàn),可正因?yàn)槿绱耍琒ayHello()方法屬于ISayHello,不屬于ChineseSayHello:


        //正確,可以編譯
        var sayHello = new ChineseSayHello() as ISayHello;
        sayHello.SayHello();

        //錯(cuò)誤,無法編譯
        var sayHello = new ChineseSayHello();
        sayHello.SayHello();


        異步流


        該特性可以看作是IEnumerable的一個(gè)延伸,即IAsyncEnumerable,主要有下面三個(gè)屬性:


        • 它是用 async 修飾符聲明的。


        • 它將返回 IAsyncEnumerable。


        • 該方法包含用于在異步流中返回連續(xù)元素的 yield return 語句。


        下面是一個(gè)來自微軟官方的基本示例:


        //生成異步流

        public static async System.Collections.Generic.IAsyncEnumerable<int> GenerateSequence()
        {
        for (int i = 0; i < 20; i++)
        {
        await Task.Delay(100);
        yield return i;
        }
        }
        //枚舉異步流
        await foreach (var number in GenerateSequence())
        {
        Console.WriteLine(number);
        }


        和異步流相關(guān)的一個(gè)概念是:異步可釋放,即 System.IAsyncDisposable,這個(gè)可以參考:實(shí)現(xiàn) DisposeAsync 方法。


        索引和范圍


        關(guān)于這個(gè),我們換一種說法,可能大家就能接受啦!是什么呢?答案是:切片。切片語法博主經(jīng)常在 Python 中使用,想不到有生之年居然可以在 C# 里用到這個(gè)語法。不過,這個(gè)語法糖怎么看都不甜啊,因?yàn)闆]那味兒!


        var words = new string[]
        {
        // index from start index from end
        "The", // 0 ^9
        "quick", // 1 ^8
        "brown", // 2 ^7
        "fox", // 3 ^6
        "jumped", // 4 ^5
        "over", // 5 ^4
        "the", // 6 ^3
        "lazy", // 7 ^2
        "dog" // 8 ^1
        };

        //取最后一個(gè)元素
        Console.WriteLine($"The last word is {words[^1]}");

        //獲取第一個(gè)元素到第三個(gè)元素
        var quickBrownFox = words[1..4];

        //獲取倒數(shù)第一個(gè)元素到倒數(shù)第二個(gè)元素
        var lazyDog = words[^2..^0];
        //獲取全部元素
        var all = words[..];

        //獲取開始到第三個(gè)元素
        var firstPhrase = words[..4];

        //獲取結(jié)束到倒數(shù)第二個(gè)元素
        var lastPhrase = words[6..];


        看起來這些東西在 Python 里都有啊,到底是哪里除了問題呢?我覺得更多的是符號上的不同吧, ^ 這個(gè)符號除了表示指數(shù)的意思以外,還有按位進(jìn)行異或運(yùn)算的意思,所以,這個(gè)語法糖加進(jìn)來以后就會(huì)顯得相當(dāng)混亂,而 .. 這個(gè)符號顯然沒有 : 寫起來方便啊,所以,雖然 C# 從 C# 8.0 開始有了切片語法,可這不是我想要的切片語法啊!


        以上,就是 C# 8.0 中我個(gè)人比較喜歡的語法特性。需要了解所有 C# 8.0 語法特性的小伙伴們,則可以參考這里:C# 8.0 中的新增功能。


        五、C# 9.0


        在 C# 9.0 中,我個(gè)人比較喜歡的特性主要有以下幾個(gè):Record、頂級語句、模式匹配增強(qiáng)。


        Record


        record 是 C# 9.0 中提供的一個(gè)新的關(guān)鍵字,地位上等同于 class 和 struct,中文翻譯為:記錄類型。這是一種引用類型,它提供合成方法來提供值語義,從而實(shí)現(xiàn)相等性。默認(rèn)情況下,記錄是不可變的。簡而言之,record 是不可變的引用類型。你可能會(huì)說,我們?yōu)槭裁匆氵@么一個(gè)類型出來呢?難道 class 不香嗎?


        我覺得如果要回答這個(gè)問題,可以借鑒 DDD 中的實(shí)體 和 值對象這兩個(gè)概念。實(shí)體 通常都有一個(gè)唯一的標(biāo)識并且在整個(gè)生命周期中具有連續(xù)性,這一類角色通過 class 來實(shí)現(xiàn)一直都工作得很好。例如,每一個(gè) User 都會(huì)有一個(gè)唯一的UserId ,我們使用 UserId 來判斷其相等性。而 值對象 則是指那些沒有唯一的標(biāo)識、不可變的、通過屬性來判斷相等性。例如,我們有一個(gè)地址 Address,它由省、市、區(qū)、縣和詳細(xì)地址組成,那么,問題來了,如果兩個(gè) Address 的省、市、區(qū)、縣和詳細(xì)地址都相同,這兩個(gè) Address 是不是同一個(gè)地址呢?常識告訴我們:不會(huì),因?yàn)樗鼈兪遣煌膶?shí)例。


        這就是 record 出現(xiàn)的原因,對于上面的這個(gè)問題,我們可以來解決:


        record Address
        {
        public string Province { get; set; }
        public string City { get; set; }
        public string District { get; set; }
        public string County { get; set; }
        }

        var addr1 = new Address() { Province = "陜西省", City = "西安市", District = "雁塔區(qū)" };
        var addr2 = new Address() { Province = "陜西省", City = "西安市", District = "雁塔區(qū)" };
        Console.WriteLine($"addr1 == addr2:{addr1 == addr2}");


        想想以前我們是怎么做的呢?是不是要寫類似下面這樣的代碼:


        if (addr1.Province == addr2.Province && addr1.City == addr2.City) {
        //屬性太多啦,我就不一個(gè)一個(gè)地比較啦,懂得都懂
        }


        所以,這就是 record 存在的意義。除此之外呢,這個(gè)關(guān)鍵字更多的是語法層面上的,實(shí)際上從編譯出來的 IL 來看,它本質(zhì)上依然是一個(gè)類,并且它是不可變的。定義記錄類型時(shí),編譯器會(huì)合成其他幾種方法:


        • 基于值的相等性比較方法


        • 替代 GetHashCode()


        • 復(fù)制和克隆成員


        • PrintMembers 和 ToString()


        那么,你可能還會(huì)有疑問,假如我定義了兩個(gè)不同的記錄類型,它們都擁有相同的屬性成員,如果按值相等來判斷的話,豈不是這兩個(gè)不同的記錄類型變成相同的了?這么重要的問題,微軟怎么可能沒有想到呢?編譯器會(huì)合成一個(gè) EqualityContract 屬性,該屬性返回與記錄類型匹配的 Type 對象。在這里,微軟再一次發(fā)揮了元組的威力,對于上面定義的地址,我們可以繼續(xù)使用解構(gòu)語法:


        (province, city, district, county) = addr1;


        當(dāng)然,我相信哪怕到2090年,這個(gè)世界上依然會(huì)有“杠精”:你說這玩意兒不能變?我就想變怎么辦?答案是使用with語法:


        public record Person
        {
        public string LastName { get; }
        public string FirstName { get; }
        public Person(string first, string last) => (FirstName, LastName) = (first, last);
        }

        var person = new Person("Bill", "Wagner");
        Person brother = person with { FirstName = "Paul" }; // 修改FirstName的副本
        Person clone = person with { }; // 空集副本


        好了,關(guān)于記錄類型就先為大家介紹到這里,更詳細(xì)的說明可以參考這里:使用記錄類型。


        頂級語句


        頂級語句,這個(gè)又是一個(gè)聽起來非常模糊的概念對不對?大家可以看一下這篇文章:26 種不同的編程語言的 “Hello World” 程序。怎么樣,在眾多解釋型的語言中,C#、Java 甚至 C++ 的 “Hello World” 是不是都看起來有一點(diǎn)臃腫?


        好了,現(xiàn)在可以夢想成真啦!


        using System;
        Console.WriteLine("Hello World!");


        如果覺得這樣還顯得臃腫,可以省略 using 部分:


        System.Console.WriteLine("Hello World!");


        當(dāng)然啦,一個(gè)項(xiàng)目里顯然只能有一個(gè)文件可以使用頂級語句,你可以理解為這些代碼運(yùn)行在一個(gè)看不見的Main()方法中,而Main()方法顯然只能有一個(gè),相比下來,Python 就自由多啦,不過if __name__ == '__main__'的老梗就不再這里展開啦!


        模式匹配增強(qiáng)


        感覺微軟在模式匹配的道路上越走越遠(yuǎn)啊,說好的語法糖呢?這簡直是毒藥,7.0 里面眼花繚亂的switch都還沒學(xué)會(huì)呢!


        public static bool IsLetter(this char c) =>
        c is >= 'a' and <= 'z' or >= 'A' and <= 'Z';
        public static bool IsLetterOrSeparator(this char c) =>
        c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z') or '.' or ',';
        if (e is not null)
        {
        // ...
        }


        以上,就是 C# 9.0 中我個(gè)人比較喜歡的語法特性。需要了解所有 C# 9.0 語法特性的小伙伴們,則可以參考這里:C# 9.0 中的新增功能。


        - EOF -







        回復(fù)?【關(guān)閉】學(xué)關(guān)
        回復(fù)?【實(shí)戰(zhàn)】獲取20套實(shí)戰(zhàn)源碼
        回復(fù)?【被刪】學(xué)個(gè)
        回復(fù)?【訪客】學(xué)
        回復(fù)?【小程序】學(xué)獲取15套【入門+實(shí)戰(zhàn)+賺錢】小程序源碼
        回復(fù)?【python】學(xué)微獲取全套0基礎(chǔ)Python知識手冊
        回復(fù)?【2019】獲取2019 .NET 開發(fā)者峰會(huì)資料PPT
        回復(fù)?【加群】加入dotnet微信交流群

        為什么不能在代碼中使用「User」這個(gè)單詞?


        臥槽:微信可以這樣換個(gè)字體了!


        點(diǎn)贊和在看就是最大的支持??

        瀏覽 53
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. 91人妻人人澡人人爽人人玩 | 一级黄色录像免费播放 | 国产一级无码Av片在线观看 | 一级黄色AA片 | 高潮喷水视频在线 |