C#8.0 新增功能

来源:https://www.cnblogs.com/taotaozhuanyong/archive/2019/10/21/11699796.html
-Advertisement-
Play Games

1、Readonly成員 可將readonly修飾符應用於結構的任何成員,它指示該成員不會修改狀態。這比將readonly修飾符應用於struct聲明更精細。 像大多數結構一樣ToString()方法不會修改狀態。可以通過readonly修飾符添加到ToString()的聲明來對此進行指示: 上述更 ...


1、Readonly成員

  可將readonly修飾符應用於結構的任何成員,它指示該成員不會修改狀態。這比將readonly修飾符應用於struct聲明更精細。

public struct Point
{
    public double X { get; set; }

    public double Y { get; set; }

    public double Distance => Math.Sqrt(X * X + Y * Y);

    public override string ToString() => $"({X}, {Y}) is {Distance} from the origin";
}

像大多數結構一樣ToString()方法不會修改狀態。可以通過readonly修飾符添加到ToString()的聲明來對此進行指示:

public readonly override string ToString() => $"({X}, {Y}) is {Distance} from the origin";

上述更改會發生編譯器警告,因為ToString訪問Distance屬性,該屬性未標記為readonly,如下圖所示:

 

 需要創建防禦性副本時,編譯器會發出警告,Distance屬性不會更改狀態,因此可以通過將readonly修飾符添加到聲明來修複此警告:

public readonly double Distance => Math.Sqrt(X * X + Y * Y);

請註意,readonly修飾符對於只讀屬性是必須的。編譯器不會假設get訪問器不修改狀態;必須明確聲明readonly,編譯器會強制實施以下規則:

  readonly成員不修改狀態,除非readonly修飾符,否則不會編譯以下方法:

public readonly void Translate(int xOffset,int yOffset)
{
    X += xOffset;
    Y += yOffset;
}

 通過此功能,可以指定設計意圖,使編譯器可以強制執行該意圖,並基於該意圖進行優化。

2、預設介面成員

 現在可以將成員添加到介面,並未這些成員提供實現。藉助次語言功能,API坐著可以將方法添加到以後版本的介面中,而不會破壞與該介面當前實現的源或耳機中南海文件相容性。現在有的實現繼承預設介面。此功能使C#與面向Android 或Swift的API進行互操作,此類API支持類似功能。預設介面成員還支持類似於“特征”語言功能的方案。

預設介面成員會影響很多方案和語言元素。https://docs.microsoft.com/zh-cn/dotnet/csharp/tutorials/default-interface-methods-versions

3、在更多位置中使用更多模式

模式匹配提供了在相關但不同類型的數據中提供形狀相關功能的工具。C#7.0通過使用is表達式和switch語句引入了類型模式和常量模式的語法。這些功能代表了支持數據和功能分離時的編程範例的初步嘗試。隨著行業轉向更多微服務和其他基於雲的體繫結構,還需要其他語言功能。

C#8.0擴展了此辭彙表。這樣就可以在代碼中的更多位置使用更多模式表達式。當數據和功能分離時,請考慮使用這些功能。當演算法依賴於對象運行時類型以外的事實時,請考慮使用模式匹配。這些技術提供了另一種表達設計的方式。

除了烤魚在新位置使用新模式之外,C#8.0還添加了“遞歸模式”。任何模式表達式的結果都是一個表達式。遞歸模式只是應用於另外一個模式表達式輸出的模式表達式。

switch表達式

通常情況下,switch語句在其每個case塊中生成一個值。藉助Switch表達式,可以使用更簡潔的表達式語句,只是些許重覆的case和break關鍵字額大括弧。以下麵列出彩虹顏色的枚舉為例:

public enum Rainbow
{
    Red,
    Orange,
    Yellow,
    Green,
    Blue,
    Indigo,
    Violet
}

如果應用定義了通過R、G、B組件構造而成的RGBColor類型,可使用以下包含switch表達式的方法,將Rainbow轉換為RGB值:

public static RGBColor FromRainbow(Rainbow colorBand) =>
colorBand switch
{
    Rainbow.Red => new RGBColor(0xFF, 0x00, 0x00),
    Rainbow.Orange => new RGBColor(0xFF, 0x7F, 0x00),
    Rainbow.Yellow => new RGBColor(0xFF, 0xFF, 0x00),
    Rainbow.Green => new RGBColor(0x00, 0xFF, 0x00),
    Rainbow.Blue => new RGBColor(0x00, 0x00, 0xFF),
    Rainbow.Indigo => new RGBColor(0x4B, 0x00, 0x82),
    Rainbow.Violet => new RGBColor(0x94, 0x00, 0xD3),
    _ => throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand)),
};

這裡有幾個語法改進:

  1、變數位於switch關鍵字之前。不同的順序使得在視覺上可以很輕鬆的區分switch表達式和switch語句。

  2、將case和:元素替換為=>,它更簡潔,更直觀。

  3、將default事例替換為_棄元。

  4、正文是表達式,不是語句。

將其與使用經典switch語句的等效代碼進行對比:

public static RGBColor FromRainbowClassic(Rainbow colorBand)
{
    switch (colorBand)
    {
        case Rainbow.Red:
            return new RGBColor(0xFF, 0x00, 0x00);
        case Rainbow.Orange:
            return new RGBColor(0xFF, 0x7F, 0x00);
        case Rainbow.Yellow:
            return new RGBColor(0xFF, 0xFF, 0x00);
        case Rainbow.Green:
            return new RGBColor(0x00, 0xFF, 0x00);
        case Rainbow.Blue:
            return new RGBColor(0x00, 0x00, 0xFF);
        case Rainbow.Indigo:
            return new RGBColor(0x4B, 0x00, 0x82);
        case Rainbow.Violet:
            return new RGBColor(0x94, 0x00, 0xD3);
        default:
            throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand));
    };
}

屬性模式:

藉助屬性模式,可以匹配所有檢查的對象的屬性。請看一個電子商務網站的事例額,該網站必鬚根據買家地址計算銷售稅,這種計算不時Address類的核心職責。它會隨時間變化,可能比地址格式的更改更頻繁,銷售稅的金額取決於地址的State屬性。下麵的方法使用屬性模式從地址和價格計算銷售稅:

public static decimal ComputeSalesTax(Address location, decimal salePrice) =>
    location switch
    {
        { State: "WA" } => salePrice * 0.06M,
        { State: "MN" } => salePrice * 0.75M,
        { State: "MI" } => salePrice * 0.05M,
        // other cases removed for brevity...
        _ => 0M
    };

 模式匹配為表達此演算法創建了簡介的語法

元素模式:

一些演算法依賴於多個輸入,使用元組模式,可以根據表示為元組的多個值進行。以下代碼顯示了游戲“rock,pack,scissors(石頭剪刀布)”的切換表達式

 public static string RockPaperScissors(string first, string second)
    => (first, second) switch
    {
        ("rock", "paper") => "rock is covered by paper. Paper wins.",
        ("rock", "scissors") => "rock breaks scissors. Rock wins.",
        ("paper", "rock") => "paper covers rock. Paper wins.",
        ("paper", "scissors") => "paper is cut by scissors. Scissors wins.",
        ("scissors", "rock") => "scissors is broken by rock. Rock wins.",
        ("scissors", "paper") => "scissors cuts paper. Scissors wins.",
        (_, _) => "tie"
    };

消息顯示獲勝者,棄元表示平局(石頭剪刀布游戲)的三種組合或其他文本輸入。

位置模式

某些類型包含Deconstruct方法,就可以使用位置模式,該方法將其屬性解構為離散變數。如果可以訪問Deconstruct方法,就可以使用位置模式檢查對象的屬性並將這些屬性用於模式。考慮以下Point類。其中包含用於為X和Y創建離散變數的Deconstruct方法:

public class Point
{
    public int X { get; set; }

    public int Y { get; set; }

    public Point(int x, int y) => (X, Y) = (x, y);

    public void Deconstruct(out int x, out int y) => (x, y) = (X, Y);
}

此外,請考慮以下表示象限的各種位置的枚舉:

 public enum Quadrant
 {
     Unknown,
     Origin,
     One,
     Two,
     Three,
     Four,
     OnBorder
 }

下麵的方法使用位置模式來提取x和y的值,然後,它使用when自居來確定改點的Quadrant:

static Quadrant GetQuadrant(Point point) => point switch
{
    (0, 0) => Quadrant.Origin,
    var (x, y) when x > 0 && y > 0 => Quadrant.One,
    var (x, y) when x < 0 && y > 0 => Quadrant.Two,
    var (x, y) when x < 0 && y < 0 => Quadrant.Three,
    var (x, y) when x > 0 && y < 0 => Quadrant.Four,
    var (_, _) => Quadrant.OnBorder,
    _ => Quadrant.Unknown
};

當x或y為0(但不是兩者同時為0)時,前一個開關中的棄元模式匹配,switch表達式必須要麼生成值,要麼引發異常。如果這些情況都不匹配,則switch表達式將引發異常。如果沒有switch表達式中涵蓋所有可能的情況,編譯器將生成一個警告。

可以在模式匹配高級教程中探索模式匹配方法。https://docs.microsoft.com/zh-cn/dotnet/csharp/tutorials/pattern-matching

4、using聲明

using聲明是前面帶using關鍵字的變數聲明。它指示編譯器聲明的變數應該在封閉範圍的末尾進行處理。以下麵編寫文本文件的代碼為例:

static void WriteLinesToFile(IEnumerable<string> lines)
{
    using var file = new System.IO.StreamWriter("WriteLines2.txt");
    foreach (var line in lines)
    {
        //如果該行不包含單詞“Second”,則將該行寫入文件
        if (!line.Contains("Second"))
        {
            file.WriteLine(line);
        }
    }
    //文件已在此處釋放
}

在前面的示例中,當到達方法的右括弧時,將對該文件進行處理,這是聲明file的範圍的末尾,前面的代碼相當於下麵使用經典using 語句的代碼:

static void WriteLinesToFile(IEnumerable<string> lines)
{
    using (var file=new System.IO.StreamWriter("WriteLines2.txt"))
    {
        foreach (var line in lines)
        {
            //如果該行不包含單詞“Second”,則將該行寫入文件
            if (!line.Contains("Second"))
            {
                file.WriteLine(line);
            }
        }
    }//文件已在此處被釋放
}

在前面的示例中,當到達與using語句關聯的右括弧時,將對該文件進行處理。在這兩種情況下,編譯器將生成對Dispose()的調用。如果using語句中的表達式不可處置,編譯器將生成一個錯誤。

5、靜態本地函數

現在可以向本地函數添加static修飾符,以確保本地函數不會從封閉範圍捕獲(引用)任何變數。這樣做會生成CS8421,“靜態本地函數不能包含對<variable>的引用”。

考慮下列代碼,本地函數LocalFunction訪問在封閉範圍(方法M)中聲明的變數y,因此,不能用static修飾符來聲明LocalFunction:

int M()
{
    int y;
    LocalFunction();
    return y;

    void LocalFunction() => y = 0;
}

如果在LocalFunction方法前面加上static:

 

 

 

下麵的代碼包含一個靜態本地函數,它可以是靜態的,因為它不訪問封閉範圍中的熱呢變數:

 int M()
 {
     int y = 5;
     int x = 7;
     return Add(x, y);

     static int Add(int left, int right) => left + right;
 }

6、可處置的ref結構

用ref修飾符聲明的struct可能無法實現任何藉口,因此無法實現IDisposable。因此,要能夠處理ref struct,它必須有一個可訪問的void Dispose()方法。這同樣適用於readonly ref struct聲明。

7、可謂空引用類型

在可謂空註釋上下文中,引用類型的任何變數都被視為不可謂空引用類型。若要指示一個變數可能為null,必須在類型名稱後面附加?,以將該變數聲明為可謂空引用類型。

如果,不用?來聲明,傳統代碼中,這樣寫,會報錯:

static void Main(string[] args)
{
    List<string> list = null;
    var newList = list.Where(s => s.Length > 2) ?? new List<string>();
    foreach (var item in newList)
    {
        Console.WriteLine(item);
    }
    Console.WriteLine("ok");
    Console.ReadLine();
}

 

 

 

改進之後,就沒問題了:

static void Main(string[] args)
{
    List<string> list = null;
    var newList = list?.Where(s => s.Length > 2) ?? new List<string>();
    foreach (var item in newList)
    {
        Console.WriteLine(item);
    }
    Console.WriteLine("ok");
    Console.ReadLine();
}

 

 

 

 對於不可為空引用類型,編譯器使用流分析來確保在聲明時將本地變數初始化為非Null值,欄位必須在構造過程中初始化,如果沒有通過調用任何可用的構造函數或通過初始化表達式來設置變數,編譯器將生成警告。此外,不能向不可為空引用類型分配一個可以為Null的值。

不對可為空引用類型進行檢查以確保他們沒有被賦予Null值或初始化為Null。不過,編譯器使用流分析來確保可為空引用類型的任何變數在被訪問或分配給不可為空引用類型之前,都會對其Null性進行檢查。

8、非同步流

從C#8.0開始,可以創建並以非同步方式使用流。返回非同步流的方法有三個屬性:

  1、它是用async修飾符聲明的

  2、它將返回IAsyncEnumerable<T>

  3、該方法包含用於在非同步流中返回連續元素的yield return 語句

使用非同步流需要在枚舉流元素時在foreach關鍵字前面添加await關鍵字。談價await關鍵字需要枚舉非同步流的方法,以使用async修飾符進行聲明並返回async方法允許的類型。通常在意味著返回Task或Task<TResult>。也可以為ValueTask或ValueTask<TResult>。方法既可以使用非同步流,也可以生成非同步流,這意味著它將返回IAsyncEnumerable<T>。下麵的代碼生成一個從0到10的序列,在生成沒個數字之間等待100毫秒:

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語句來枚舉序列:

 public static async void TestGenerateSequence()
 {
     await foreach (var item in GenerateSequence())
     {
         Console.WriteLine(item);
     }
 }

9、索引和範圍

 範圍和索引為在數組中指定資範圍(Span<T>或ReadOnlySpan<T>)提供了簡潔語法。

此語言支持依賴於兩個新類型和兩個新運算符。

  1、System.Index表示一個序列索引

  2、^運算符,指定一個索引與序列末尾相關。

  3、System.Range表示序列的子範圍

  4、範圍運算符(..),用於指定範圍的開始和末尾,就像操作數一樣。

讓我們從索引規則開始。請考慮數組sequence。0索引與sequence[0]相同。^0索引與sequence[sequence.Length]相同。請註意,sequence[^0]不會引發異常,就像是sequence[sequence.Length]一樣。對於任何數字n,索引^n與sequence.Length-n相同。

範圍指定範圍的開始和末尾。包括此範圍的開始,但是不包括此範圍的末尾,這表示此範圍包含開始但不包含末尾。範圍[0..^0]表示整個範圍,就像[0..sequence.Length]表示整個範圍。

請看以下一個示例,請考慮以下數組,用其順數索引和倒數索引進行註釋:

 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
 };              // 9 (or words.Length) ^0

可以使用^1索引檢索最後一個詞:

 Console.WriteLine($"The last word is {words[^1]}");

 

 以下代碼創建了一個包含單詞"quick"、"brown"和"fox"的子範圍。它包括words[1]到words[3],元素words[4]不在此範圍內。

var quickBrownFox = words[1..4];
foreach (var item in quickBrownFox)
{
    Console.WriteLine(item);
}

 

 以下代碼使用"lazy"和"dog"創建一個子範圍,它包括words[^2]和words[^1]。不包括結束索引words[^0]:

var lazyDog = words[^2..^0];

下麵的示例為開始和/或結束創建了開放範圍:

var allWords = words[..];      // contains "The" through "dog".
var firstPhrase = words[..4];  // contains "The" through "fox"
var lastPhrase = words[6..];   // contains "the", "lazy" and "dog"

此外可以將範圍聲明為變數:

Range phrase = 1..4;

然後可以在[和]字元中使用該範圍:

var text = words[phrase];

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 問題: 如果 search 在 dist 中順序出現而不要求連續出現,那代碼應該如何修改?如何計算這種匹配的可能性? 數組 search=[5,4,6],在數據 dist=[1,5,5,4,3,4,5,6]的起始位置是1 (因為dist下標{1,3,7}和下標{1,5,7}的元素都等於 search ...
  • 比如: 12321,第一位等於第五位,第二位等於第四位 ...
  • 1.首先登陸釘釘開發者後臺 https://ding-doc.dingtalk.com/ 2.選擇H5微應用,創建應用 4.創建好之後,查看所建好的應用信息 其中AgentId,AppKey,AppSecret很重要,調用時需要用到 5.直接上代碼看效果 ok,可以看到已經收到消息了這裡需要註意的是 ...
  • 前端 後端 技術要點: 1.上傳附件<input type="file" name="goodsfile" id="goodsfile" class="layui-input" accept="application/vnd.openxmlformats-officedocument.spreads ...
  • 微軟官方概述: 在C 中,協變和逆變能夠實現數組類型、委托類型和泛型類型參數的隱式引用轉換。協變保留分配相容性,逆變則與之相反。 協變:能夠使用與原始指定的派生類型相比,派生程度更大的類型。 逆變:能夠使用派生程度更小的類型。 官方示例: 上面示例中,從 string object 的隱式轉換這是協 ...
  • 這篇文章粗略指引怎麼搭建.Net Core API,並使用Swagger服務。非常適合初學者,網上也有很多。 ...
  • 在文檔屬性中,可以設置諸多關於文檔的信息,如創建時間、作者、單位、類別、關鍵詞、備註等摘要信息以及一些自定義的文檔屬性。下麵將通過C#程式來演示如何設置,同時對文檔內的已有信息,也可以實現讀取或刪除等操作。 示例大綱: 1. 添加文檔屬性 1.1 添加摘要信息 1.2 添加自定義文檔信息 2. 讀取 ...
  • 轉發:https://www.cnblogs.com/Ninputer/archive/2008/11/22/generic_covariant.html 背景知識:協變和逆變 假設有這樣兩個類型:TSub是TParent的子類,顯然TSub型引用是可以安全轉換為TParent型引用的。如果一個泛型 ...
一周排行
    -Advertisement-
    Play Games
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...