<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          一文看懂 C# 10的新特性

          共 9832字,需瀏覽 20分鐘

           ·

          2022-02-17 23:46


          前言


          們很高興地宣布 C# 10 作為 .NET 6 和 Visual Studio 2022的一部分已經(jīng)發(fā)布了。在這篇文章中,我們將介紹 C# 10 的許多新功能,這些功能使你的代碼更漂亮、更具表現(xiàn)力、更快。閱讀 Visual Studio 2022 公告和.NET 6 公告以了解更多信息,包括如何安裝。


          Visual Studio 2022?公告:https://aka.ms/vs2022gablog


          .NET 6:https://aka.ms/dotnet6-GA


          全局和隱式 usings


          using 指令簡(jiǎn)化了您使用命名空間的方式。C# 10 包括一個(gè)新的全局 using 指令和隱式 usings,以減少您需要在每個(gè)文件頂部指定的 usings 數(shù)量。


          全局 using 指令


          如果關(guān)鍵字 global 出現(xiàn)在 using 指令之前,則 using 適用于整個(gè)項(xiàng)目:

          global?using?System;

          你可以在全局 using 指令中使用 using 的任何功能。例如,添加靜態(tài)導(dǎo)入類型并使該類型的成員和嵌套類型在整個(gè)項(xiàng)目中可用。如果您在using 指令中使用別名,該別名也會(huì)影響您的整個(gè)項(xiàng)目:

          global?using?static?System.Console;
          global?using?Env?=?System.Environment;

          您可以將全局使用放在任何 .cs 文件中,包括 Program.cs 或?qū)iT命名的文件,如 globalusings.cs。全局 usings 的范圍是當(dāng)前編譯,一般對(duì)應(yīng)當(dāng)前項(xiàng)目。

          有關(guān)詳細(xì)信息,請(qǐng)參閱全局 using 指令。

          • 全局?using?指令https://docs.microsoft.com/dotnet/csharp/languagereference/keywords/using-directive#global-modifier

          隱式 usings

          隱式 usings 功能會(huì)自動(dòng)為您正在構(gòu)建的項(xiàng)目類型添加通用的全局 using 指令。要啟用隱式 usings,請(qǐng)?jiān)?.csproj 文件中設(shè)置 ImplicitUsings 屬性:

          <PropertyGroup>
          ????
          ????<ImplicitUsings>enableImplicitUsings>
          PropertyGroup>

          在新的 .NET 6 模板中啟用了隱式 usings 。在此博客文章中閱讀有關(guān) .NET 6 模板更改的更多信息。

          一些特定全局 using 指令集取決于您正在構(gòu)建的應(yīng)用程序的類型。例如,控制臺(tái)應(yīng)用程序或類庫(kù)的隱式 usings 不同于 ASP.NET 應(yīng)用程序的隱式 usings。

          有關(guān)詳細(xì)信息,請(qǐng)參閱此隱式usings文章。

          • 博客文章

            https://devblogs.microsoft.com/dotnet/announcing-net-6-preview-7/#net-sdk-c-project-templates-modernized

          • 隱式usings

            https://docs.microsoft.com/en-us/dotnet/core/project-sdk/overview#implicit-using-directives

          Combining?using 功能

          文件頂部的傳統(tǒng) using 指令、全局 using 指令和隱式 using 可以很好地協(xié)同工作。隱式 using 允許您在項(xiàng)目文件中包含適合您正在構(gòu)建的項(xiàng)目類型的 .NET 命名空間。全局 using 指令允許您包含其他命名空間,以使它們?cè)谡麄€(gè)項(xiàng)目中可用。代碼文件頂部的 using 指令允許您包含項(xiàng)目中僅少數(shù)文件使用的命名空間。

          無論它們是如何定義的,額外的 using 指令都會(huì)增加名稱解析中出現(xiàn)歧義的可能性。如果遇到這種情況,請(qǐng)考慮添加別名或減少要導(dǎo)入的命名空間的數(shù)量。例如,您可以將全局 using 指令替換為文件子集頂部的顯式 using 指令。

          如果您需要?jiǎng)h除通過隱式 usings 包含的命名空間,您可以在項(xiàng)目文件中指定它們:

          <ItemGroup>
          ??<Using?Remove="System.Threading.Tasks"?/>
          ItemGroup>

          您還可以添加命名空間,就像它們是全局 using 指令一樣,您可以將 Using 項(xiàng)添加到項(xiàng)目文件中,例如:

          <ItemGroup>
          ??<Using?Include="System.IO.Pipes"?/>
          ItemGroup>

          文件范圍的命名空間

          許多文件包含單個(gè)命名空間的代碼。從 C# 10?開始,您可以將命名空間作為語句包含在內(nèi),后跟分號(hào)且不帶花括號(hào):

          namespace?MyCompany.MyNamespace;

          class?MyClass?//?Note:?no?indentation
          {?...?}?

          他簡(jiǎn)化了代碼并刪除了嵌套級(jí)別。只允許一個(gè)文件范圍的命名空間聲明,并且它必須在聲明任何類型之前出現(xiàn)。

          有關(guān)文件范圍命名空間的更多信息,請(qǐng)參閱命名空間關(guān)鍵字文章。

          • 命名空間關(guān)鍵字文章https://docs.microsoft.com/dotnet/csharp/languagereference/keywords/namespace

          對(duì)?lambda 表達(dá)式和方法組的改進(jìn)

          我們對(duì) lambda 的語法和類型進(jìn)行了多項(xiàng)改進(jìn)。我們預(yù)計(jì)這些將廣泛有用,并且驅(qū)動(dòng)方案之一是使 ASP.NET Minimal API 更加簡(jiǎn)單。

          • lambda?的語法

            https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-10#lambda-expression-improvements

          • ASP.NET?Minimal APIhttps://devblogs.microsoft.com/dotnet/announcing-asp-net-core-in-net-6/

          lambda?的自然類型

          Lambda 表達(dá)式現(xiàn)在有時(shí)具有“自然”類型。這意味著編譯器通常可以推斷出 lambda 表達(dá)式的類型。

          到目前為止,必須將 lambda 表達(dá)式轉(zhuǎn)換為委托或表達(dá)式類型。在大多數(shù)情況下,您會(huì)在 BCL 中使用重載的 Func<...> 或 Action<...> 委托類型之一:

          Func<string,?int>?parse?=?(string?s)?=>?int.Parse(s);

          但是,從 C# 10 開始,如果 lambda 沒有這樣的“目標(biāo)類型”,我們將嘗試為您計(jì)算一個(gè):

          var?parse?=?(string?s)?=>?int.Parse(s);

          你可以在你最喜歡的編輯器中將鼠標(biāo)懸停在 var parse 上,然后查看類型仍然是 Func。一般來說,編譯器將使用可用的 Func 或 Action 委托(如果存在合適的委托)。否則,它將合成一個(gè)委托類型(例如,當(dāng)您有 ref 參數(shù)或有大量參數(shù)時(shí))。

          并非所有 lambda 表達(dá)式都有自然類型——有些只是沒有足夠的類型信息。?例如,放棄參數(shù)類型將使編譯器無法決定使用哪種委托類型:

          var?parse?=?s?=>?int.Parse(s);?//?ERROR:?Not?enough?type?info?in?the?lambda

          lambda 的自然類型意味著它們可以分配給較弱的類型,例如 object 或 Delegate:

          object?parse?=?(string?s)?=>?int.Parse(s);???//?Func
          Delegate?parse?=?(string?s)?=>?int.Parse(s);?//?Func

          當(dāng)涉及到表達(dá)式樹時(shí),我們結(jié)合了“目標(biāo)”和“自然”類型。如果目標(biāo)類型是LambdaExpression 或非泛型 Expression(所有表達(dá)式樹的基類型)并且 lambda 具有自然委托類型 D,我們將改為生成 Expression

          LambdaExpression?parseExpr?=?(string?s)?=>?int.Parse(s);?//?Expression>
          Expression?parseExpr?=?(string?s)?=>?int.Parse(s);???????//?Expression>

          方法組的自然類型

          法組(即沒有參數(shù)列表的方法名稱)現(xiàn)在有時(shí)也具有自然類型。您始終能夠?qū)⒎椒ńM轉(zhuǎn)換為兼容的委托類型:

          Func<int>?read?=?Console.Read;
          Action?write?=?Console.Write;

          現(xiàn)在,如果方法組只有一個(gè)重載,它將具有自然類型:

          var?read?=?Console.Read;?//?Just?one?overload;?Func?inferred
          var?write?=?Console.Write;?//?ERROR:?Multiple?overloads,?can't?choose

          lambda?的返回類型

          在前面的示例中,lambda 表達(dá)式的返回類型是顯而易見的,并被推斷出來的。情況并非總是如此:

          var?choose?=?(bool?b)?=>?b???1?:?"two";?//?ERROR:?Can't?infer?return?type

          在 C# 10 中,您可以在 lambda 表達(dá)式上指定顯式返回類型,就像在方法或本地函數(shù)上一樣。返回類型在參數(shù)之前。當(dāng)你指定一個(gè)顯式的返回類型時(shí),參數(shù)必須用括號(hào)括起來,這樣編譯器或其他開發(fā)人員不會(huì)太混淆:

          var?choose?=?object?(bool?b)?=>?b???1?:?"two";?//?Func

          lambda 上的屬性

          從 C# 10 開始,您可以將屬性放在 lambda 表達(dá)式上,就像對(duì)方法和本地函數(shù)一樣。當(dāng)有屬性時(shí),lambda 的參數(shù)列表必須用括號(hào)括起來:

          Func<string,?int>?parse?=?[Example(1)]?(s)?=>?int.Parse(s);
          var?choose?=?[Example(2)][Example(3)]?object?(bool?b)?=>?b???1?:?"two";

          就像本地函數(shù)一樣,如果屬性在 AttributeTargets.Method 上有效,則可以將屬性應(yīng)用于 lambda。

          Lambda 的調(diào)用方式與方法和本地函數(shù)不同,因此在調(diào)用 lambda 時(shí)屬性沒有任何影響。但是,lambdas 上的屬性對(duì)于代碼分析仍然有用,并且可以通過反射發(fā)現(xiàn)它們。

          structs?的改進(jìn)

          C# 10 為 structs 引入了功能,可在 structs (結(jié)構(gòu))和類之間提供更好的奇偶性。這些新功能包括無參數(shù)構(gòu)造函數(shù)、字段初始值設(shè)定項(xiàng)、記錄結(jié)構(gòu)和 with 表達(dá)式。

          01 無參數(shù)結(jié)構(gòu)構(gòu)造函數(shù)和字段初始值設(shè)定項(xiàng)

          在 C# 10 之前,每個(gè)結(jié)構(gòu)都有一個(gè)隱式的公共無參數(shù)構(gòu)造函數(shù),該構(gòu)造函數(shù)將結(jié)構(gòu)的字段設(shè)置為默認(rèn)值。在結(jié)構(gòu)上創(chuàng)建無參數(shù)構(gòu)造函數(shù)是錯(cuò)誤的。

          從 C# 10 開始,您可以包含自己的無參數(shù)結(jié)構(gòu)構(gòu)造函數(shù)。如果您不提供,則將提供隱式無參數(shù)構(gòu)造函數(shù)以將所有字段設(shè)置為默認(rèn)值。您在結(jié)構(gòu)中創(chuàng)建的無參數(shù)構(gòu)造函數(shù)必須是公共的并且不能是部分的:

          public?struct?Address
          {
          ????public?Address()
          ????{
          ????????City?=?"";
          ????}
          ????public?string?City?{?get;?init;?}
          }

          您可以如上所述在無參數(shù)構(gòu)造函數(shù)中初始化字段,也可以通過字段或?qū)傩猿跏蓟绦虺跏蓟鼈儯?/p>

          public?struct?Address
          {
          ????public?string?City?{?get;?init;?}?=?"";
          }

          通過默認(rèn)創(chuàng)建或作為數(shù)組分配的一部分創(chuàng)建的結(jié)構(gòu)會(huì)忽略顯式無參數(shù)構(gòu)造函數(shù),并始終將結(jié)構(gòu)成員設(shè)置為其默認(rèn)值。有關(guān)結(jié)構(gòu)中無參數(shù)構(gòu)造函數(shù)的更多信息,請(qǐng)參閱結(jié)構(gòu)類型。

          02 Record structs

          從 C# 10 開始,現(xiàn)在可以使用 record struct 定義 record。這些類似于 C# 9 中引入的record 類:

          public?record?struct?Person
          {
          ????public?string?FirstName?{?get;?init;?}
          ????public?string?LastName?{?get;?init;?}
          }

          您可以繼續(xù)使用 record 定義記錄類,也可以使用 record 類來清楚地說明。

          結(jié)構(gòu)已經(jīng)具有值相等——當(dāng)你比較它們時(shí),它是按值。記錄結(jié)構(gòu)添加 IEquatable 支持和 == 運(yùn)算符。記錄結(jié)構(gòu)提供 IEquatable 的自定義實(shí)現(xiàn)以避免反射的性能問題,并且它們包括記錄功能,如 ToString() 覆蓋。

          記錄結(jié)構(gòu)可以是位置的,主構(gòu)造函數(shù)隱式聲明公共成員:

          public?record?struct?Person(string?FirstName,?string?LastName);

          主構(gòu)造函數(shù)的參數(shù)成為記錄結(jié)構(gòu)的公共自動(dòng)實(shí)現(xiàn)屬性。與 record 類不同,隱式創(chuàng)建的屬性是讀/寫的。這使得將元組轉(zhuǎn)換為命名類型變得更加容易。將返回類型從 (string FirstName, string LastName) 之類的元組更改為 Person 的命名類型可以清理您的代碼并保證成員名稱一致。聲明位置記錄結(jié)構(gòu)很容易并保持可變語義。

          如果您聲明一個(gè)與主要構(gòu)造函數(shù)參數(shù)同名的屬性或字段,則不會(huì)合成任何自動(dòng)屬性并使用您的。

          要?jiǎng)?chuàng)建不可變的記錄結(jié)構(gòu),請(qǐng)將 readonly 添加到結(jié)構(gòu)(就像您可以添加到任何結(jié)構(gòu)一樣)或?qū)?readonly 應(yīng)用于單個(gè)屬性。對(duì)象初始化器是可以設(shè)置只讀屬性的構(gòu)造階段的一部分。這只是使用不可變記錄結(jié)構(gòu)的一種方法:

          var?person?=?new?Person?{?FirstName?=?"Mads",?LastName?=?"Torgersen"};

          public?readonly?record?struct?Person
          {
          ????public?string?FirstName?{?get;?init;?}
          ????public?string?LastName?{?get;?init;?}
          }

          在本文中了解有關(guān)記錄結(jié)構(gòu)的更多信息。

          • 記錄結(jié)構(gòu)

          https://docs.microsoft.com/dotnet/csharp/language-reference/builtin-types/record

          03 Record類中 ToString () 上的密封修飾符

          記錄類也得到了改進(jìn)。從 C# 10 開始,ToString() 方法可以包含 seal 修飾符,這會(huì)阻止編譯器為任何派生記錄合成 ToString 實(shí)現(xiàn)。

          在本文中的記錄中了解有關(guān) ToString () 的更多信息。

          • 有關(guān)?ToString ()?的更多信息

            https://docs.microsoft.com/dotnet/csharp/language-reference/builtin-types/record#built-in-formatting-for-display

          04 結(jié)構(gòu)和匿名類型的表達(dá)式

          C# 10?支持所有結(jié)構(gòu)的 with 表達(dá)式,包括記錄結(jié)構(gòu),以及匿名類型:

          var?person2?=?person?with?{?LastName?=?"Kristensen"?};

          這將返回一個(gè)具有新值的新實(shí)例。您可以更新任意數(shù)量的值。您未設(shè)置的值將保留與初始實(shí)例相同的值。
          在本文中了解有關(guān)?with?的更多信息

          • 了解有關(guān)?with?的更多信息

          ttps://docs.microsoft.com/dotnet/csharp/language-reference/builtin-types/record#built-in-formatting-for-display


          內(nèi)插字符串改進(jìn)

          當(dāng)我們?cè)?C# 中添加內(nèi)插字符串時(shí),我們總覺得在性能和表現(xiàn)力方面,使用該語法可以做更多事情。

          01 內(nèi)插字符串處理程序

          今天,編譯器將內(nèi)插字符串轉(zhuǎn)換為對(duì) string.Format 的調(diào)用。這會(huì)導(dǎo)致很多分配——參數(shù)的裝箱、參數(shù)數(shù)組的分配,當(dāng)然還有結(jié)果字符串本身。此外,它在實(shí)際插值的含義上沒有任何回旋余地。

          在 C# 10 中,我們添加了一個(gè)庫(kù)模式,允許 API “接管”對(duì)內(nèi)插字符串參數(shù)表達(dá)式的處理。例如,考慮 StringBuilder.Append:

          var?sb?=?new?StringBuilder();
          sb.Append($"Hello?{args[0]},?how?are?you?");

          到目前為止,這將使用新分配和計(jì)算的字符串調(diào)用 Append(string? value) 重載,將其附加到 StringBuilder 的一個(gè)塊中。但是,Append 現(xiàn)在有一個(gè)新的重載 Append(refStringBuilder.AppendInterpolatedStringHandler handler),當(dāng)使用內(nèi)插字符串作為參數(shù)時(shí),它優(yōu)先于字符串重載。

          通常,當(dāng)您看到 SomethingInterpolatedStringHandler 形式的參數(shù)類型時(shí),API 作者在幕后做了一些工作,以更恰當(dāng)?shù)靥幚聿逯底址詽M足其目的。在我們的 Append 示例中,字符串 “Hello”、args[0] 和“,how are you?” 將單獨(dú)附加到 StringBuilder 中,這樣效率更高且結(jié)果相同。

          有時(shí)您只想在特定條件下完成構(gòu)建字符串的工作。一個(gè)例子是 Debug.Assert:

          Debug.Assert(condition,?$"{SomethingExpensiveHappensHere()}");

          在大多數(shù)情況下,條件為真,第二個(gè)參數(shù)未使用。但是,每次調(diào)用都會(huì)計(jì)算所有參數(shù),從而不必要地減慢執(zhí)行速度。Debug.Assert 現(xiàn)在有一個(gè)帶有自定義插值字符串構(gòu)建器的重載,它確保第二個(gè)參數(shù)甚至不被評(píng)估,除非條件為假。

          最后,這是一個(gè)在給定調(diào)用中實(shí)際更改字符串插值行為的示例:String.Create() 允許您指定 IFormatProvider 用于格式化插值字符串參數(shù)本身的洞中的表達(dá)式:

          String.Create(CultureInfo.InvariantCulture,?$"The?result?is?{result}");

          你可以在本文和有關(guān)創(chuàng)建自定義處理程序的本教程中了解有關(guān)內(nèi)插字符串處理程序的更多信息。

          • 創(chuàng)建自定義處理程序
            https://docs.microsoft.com/dotnet/csharp/languagereference/tokens/interpolated#compilation-of-interpolated-strings

          • 內(nèi)插字符串處理程序的更多信息

          https://docs.microsoft.com/dotnet/csharp/whats-new/tutorials/interpolated-string-handler

          02 常量?jī)?nèi)插字符串

          如果內(nèi)插字符串的所有洞都是常量字符串,那么生成的字符串現(xiàn)在也是常量。這使您可以在更多地方使用字符串插值語法,例如屬性:

          [Obsolete($"Call?{nameof(Discard)}?instead")]

          請(qǐng)注意,必須用常量字符串填充洞。其他類型,如數(shù)字或日期值,不能使用,因?yàn)樗鼈儗?duì)文化敏感,并且不能在編譯時(shí)計(jì)算。

          其他改進(jìn)

          C# 10 對(duì)整個(gè)語言進(jìn)行了許多較小的改進(jìn)。其中一些只是使 C#?以您期望的方式工作。

          在解構(gòu)中混合聲明和變量

          在 C# 10 之前,解構(gòu)要求所有變量都是新的,或者所有變量都必須事先聲明。在 C# 10 中,您可以混合:

          int?x2;
          int?y2;
          (x2,?y2)?=?(0,?1);???????//?Works?in?C#?9
          (var?x,?var?y)?=?(0,?1);?//?Works?in?C#?9
          (x2,?var?y3)?=?(0,?1);???//?Works?in?C#?10?onwards?

          在有關(guān)解構(gòu)的文章中了解更多信息。

          改進(jìn)的明確分配

          如果您使用尚未明確分配的值,C# 會(huì)產(chǎn)生錯(cuò)誤。C# 10 可以更好地理解您的代碼并且產(chǎn)生更少的虛假錯(cuò)誤。這些相同的改進(jìn)還意味著您將看到更少的針對(duì)空引用的虛假錯(cuò)誤和警告。

          在 C# 10?中的新增功能文章中了解有關(guān) C#?確定賦值的更多信息。

          • C# 10?中的新增功能文章https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-10#improved-definite-assignment

          擴(kuò)展的屬性模式

          C# 10 添加了擴(kuò)展屬性模式,以便更輕松地訪問模式中的嵌套屬性值。例如,如果我們?cè)谏厦娴?Person 記錄中添加一個(gè)地址,我們可以通過以下兩種方式進(jìn)行模式匹配:

          object?obj?=?new?Person
          {
          ????FirstName?=?"Kathleen",
          ????LastName?=?"Dollard",
          ????Address?=?new?Address?{?City?=?"Seattle"?}
          };

          if?(obj?is?Person?{?Address:?{?City:?"Seattle"?}?})
          ????Console.WriteLine("Seattle");

          if?(obj?is?Person?{?Address.City:?"Seattle"?})?//?Extended?property?pattern
          ????Console.WriteLine("Seattle");

          擴(kuò)展屬性模式簡(jiǎn)化了代碼并使其更易于閱讀,尤其是在匹配多個(gè)屬性時(shí)。

          在模式匹配文章中了解有關(guān)擴(kuò)展屬性模式的更多信息。

          • 模式匹配文章

            https://docs.microsoft.com/dotnet/csharp/languagereference/operators/patterns#property-pattern

          調(diào)用者表達(dá)式屬性

          CallerArgumentExpressionAttribute 提供有關(guān)方法調(diào)用上下文的信息。與其他 CompilerServices 屬性一樣,此屬性應(yīng)用于可選參數(shù)。在這種情況下,一個(gè)字符串:

          void?CheckExpression(bool?condition,?
          ????[CallerArgumentExpression("condition"
          )]?string??message
          ?=?null?)
          {
          ????Console.WriteLine($"Condition:?{message}");
          }

          傳遞給 CallerArgumentExpression 的參數(shù)名稱是不同參數(shù)的名稱。作為參數(shù)傳遞給該參數(shù)的表達(dá)式將包含在字符串中。例如,

          var?a?=?6;
          var?b?=?true;
          CheckExpression(true);
          CheckExpression(b);
          CheckExpression(a?>?5);

          //?Output:
          //?Condition:?true
          //?Condition:?b
          //?Condition:?a?>?5

          ArgumentNullException.ThrowIfNull()?是如何使用此屬性的一個(gè)很好的示例。它通過默認(rèn)提供的值來避免必須傳入?yún)?shù)名稱:

          void?MyMethod(object?value)
          {
          ????ArgumentNullException.ThrowIfNull(value);
          }
          • 了解有關(guān) CallerArgumentExpressionAttribute 的更多信息

            https://docs.microsoft.com/dotnet/csharp/languagereference/attributes/caller-information#argument-expressions

          歡迎在下方留言,告訴我們你的建議或想法,謝謝!


          轉(zhuǎn)自:微軟中國(guó)MSDN

          鏈接:docs.microsoft.com/zh-cn/dotnet/csharp/whats-new/csharp-10

          瀏覽 59
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  操逼网在线看 | 国产一线二线三线在线 | 中文字幕一区二区三区四区五区 | 对白av| 亚洲色老板 |