郑涛说事 發表於 2020-2-28 23:54:00

代码演示C#各版本新功能

<h1 id="代码演示c各版本新功能">代码演示C#各版本新功能</h1>
<p><code>C#</code>各版本新功能其实都能在官网搜到,但很少有人整理在一起,并通过<strong>非常简短</strong>的代码将每个新特性演示出来。</p>
<ul>
<li>代码演示C#各版本新功能
<ul>
<li>C# 2.0版 - 2005
<ul>
<li>泛型</li>
<li>分部类型</li>
<li>匿名方法</li>
<li>可以为null的值类型</li>
<li>迭代器</li>
<li>协变和逆变</li>
</ul>
</li>
<li>C# 3.0版 - 2007
<ul>
<li>自动实现的属性</li>
<li>匿名类型</li>
<li>查询表达式(LINQ)</li>
<li>Lambda表达式</li>
<li>表达式树</li>
<li>扩展方法</li>
<li>var</li>
<li>分部方法</li>
<li>对象和集合初始值设定项</li>
</ul>
</li>
<li>C# 4.0版 - 2010
<ul>
<li>dynamic</li>
<li>命名参数/可选参数</li>
<li>泛型中的协变和逆变</li>
<li>类型等效、内置互操作类型</li>
</ul>
</li>
<li>C# 5.0版 - 2012
<ul>
<li>async/await</li>
<li>调用方信息</li>
</ul>
</li>
<li>C# 6.0版 - 2015
<ul>
<li>静态导入</li>
<li>异常筛选器</li>
<li>自动初始化表达式</li>
<li>Expression-bodied 函数成员</li>
<li>Null传播器</li>
<li>字符串内插</li>
<li><code>nameof</code>表达式</li>
<li>索引初始值设定项</li>
</ul>
</li>
<li>C# 7.0版本 - 2017
<ul>
<li>out变量</li>
<li>元组和解构</li>
<li>模式匹配</li>
<li>本地函数</li>
<li>更多的expression-bodied成员</li>
<li>Ref 局部变量和返回结果</li>
<li>弃元</li>
<li>二进制文本和数字分隔符</li>
<li>throw表达式</li>
</ul>
</li>
<li>C# 8.0 版 - 2019
<ul>
<li>Readonly 成员</li>
<li>默认接口方法</li>
<li>模式匹配增强
<ul>
<li>属性模式</li>
<li>Tuple模式</li>
<li>位置模式</li>
</ul>
</li>
<li>switch表达式</li>
<li>using声明</li>
<li>静态本地函数</li>
<li>异步流</li>
<li>索引和范围</li>
<li>Null合并赋值</li>
<li>非托管构造类型</li>
<li>嵌套表达式中的 stackalloc</li>
</ul>
</li>
</ul>
</li>
<li>附录/总结</li>
</ul>
<h2 id="c-20版---2005">C# 2.0版 - 2005</h2>
<h3 id="泛型">泛型</h3>
<p><code>Java</code>中的泛型不支持值类型,且会运行时类型擦除,这一点<code>.NET</code>更优秀。</p>
<pre><code class="language-cs">// Declare the generic class.
public class GenericList&lt;T&gt;
{
    public void Add(T input) { }
}
class TestGenericList
{
    private class ExampleClass { }
    static void Main()
    {
      // Declare a list of type int.
      GenericList&lt;int&gt; list1 = new GenericList&lt;int&gt;();
      list1.Add(1);

      // Declare a list of type string.
      GenericList&lt;string&gt; list2 = new GenericList&lt;string&gt;();
      list2.Add("");

      // Declare a list of type ExampleClass.
      GenericList&lt;ExampleClass&gt; list3 = new GenericList&lt;ExampleClass&gt;();
      list3.Add(new ExampleClass());
    }
}
</code></pre>
<h3 id="分部类型">分部类型</h3>
<blockquote>
<p>拆分一个类、一个结构、一个接口或一个方法的定义到两个或更多的文件中是可能的。 每个源文件包含类型或方法定义的一部分,编译应用程序时将把所有部分组合起来。</p>
</blockquote>
<pre><code class="language-cs">public partial class Employee
{
    public void DoWork()
    {
    }
}

public partial class Employee
{
    public void GoToLunch()
    {
    }
}
</code></pre>
<h3 id="匿名方法">匿名方法</h3>
<pre><code class="language-cs">Func&lt;int, int, int&gt; sum = delegate (int a, int b) { return a + b; };
Console.WriteLine(sum(3, 4));// output: 7
</code></pre>
<h3 id="可以为null的值类型">可以为null的值类型</h3>
<pre><code class="language-cs">double? pi = 3.14;
char? letter = 'a';

int m2 = 10;
int? m = m2;

bool? flag = null;

// An array of a nullable type:
int?[] arr = new int?;
</code></pre>
<h3 id="迭代器">迭代器</h3>
<pre><code class="language-cs">static void Main()
{
    foreach (int number in SomeNumbers())
    {
      Console.Write(number.ToString() + " ");
    }
    // Output: 3 5 8
    Console.ReadKey();
}

public static System.Collections.IEnumerable SomeNumbers()
{
    yield return 3;
    yield return 5;
    yield return 8;
}
</code></pre>
<h3 id="协变和逆变">协变和逆变</h3>
<blockquote>
<p>在 C# 中,协变和逆变能够实现数组类型、委托类型和泛型类型参数的隐式引用转换。 协变保留分配兼容性,逆变则与之相反。</p>
</blockquote>
<pre><code class="language-cs">// Assignment compatibility.   
string str = "test";
// An object of a more derived type is assigned to an object of a less derived type.   
object obj = str;

// Covariance.   
IEnumerable&lt;string&gt; strings = new List&lt;string&gt;();
// An object that is instantiated with a more derived type argument   
// is assigned to an object instantiated with a less derived type argument.   
// Assignment compatibility is preserved.   
IEnumerable&lt;object&gt; objects = strings;

// Contravariance.            
// Assume that the following method is in the class:   
// static void SetObject(object o) { }   
Action&lt;object&gt; actObject = SetObject;
// An object that is instantiated with a less derived type argument   
// is assigned to an object instantiated with a more derived type argument.   
// Assignment compatibility is reversed.   
Action&lt;string&gt; actString = actObject;
</code></pre>
<h2 id="c-30版---2007">C# 3.0版 - 2007</h2>
<h3 id="自动实现的属性">自动实现的属性</h3>
<pre><code class="language-cs">// This class is mutable. Its data can be modified from
// outside the class.
class Customer
{
    // Auto-implemented properties for trivial get and set
    public double TotalPurchases { get; set; }
    public string Name { get; set; }
    public int CustomerID { get; set; }

    // Constructor
    public Customer(double purchases, string name, int ID)
    {
      TotalPurchases = purchases;
      Name = name;
      CustomerID = ID;
    }

    // Methods
    public string GetContactInfo() { return "ContactInfo"; }
    public string GetTransactionHistory() { return "History"; }

    // .. Additional methods, events, etc.
}

class Program
{
    static void Main()
    {
      // Intialize a new object.
      Customer cust1 = new Customer(4987.63, "Northwind", 90108);

      // Modify a property.
      cust1.TotalPurchases += 499.99;
    }
}
</code></pre>
<h3 id="匿名类型">匿名类型</h3>
<pre><code class="language-cs">var v = new { Amount = 108, Message = "Hello" };

// Rest the mouse pointer over v.Amount and v.Message in the following
// statement to verify that their inferred types are int and n .
Console.WriteLine(v.Amount + v.Message);
</code></pre>
<h3 id="查询表达式linq">查询表达式(LINQ)</h3>
<p><code>LINQ</code>允许你可以像写<code>SQL</code>一样写<code>C#</code>代码,像这样:</p>
<pre><code class="language-cs">from p in persons
where p.Age &gt; 18 &amp;&amp; p.IsBeatiful
select new
{
    p.WeChatId,
    p.PhoneNumber
}
</code></pre>
<p><code>LINQ</code>的意义在于让<code>C#</code>做出了重大调整,本章中说到的<code>lambda</code>表达式、扩展方法、表达式树、匿名类型、自动属性等,都是<code>LINQ</code>的必要组成部分。</p>
<p>由于用扩展方法的形式也能得到一致的结果,而且还能让代码风格更加一致,所以我平时用<code>LINQ</code>语法较少:</p>
<pre><code class="language-cs">// 与上文代码相同,但改成了扩展方法风格:
persons
    .Where(x =&gt; x.Age &gt; 18 &amp;&amp; x.IsBeatiful)
    .Select(x =&gt; new
    {
      x.WeChatId,
      x.PhoneNumber,
    });
</code></pre>
<h3 id="lambda表达式">Lambda表达式</h3>
<pre><code class="language-cs">Func&lt;int, int&gt; square = x =&gt; x * x;
Console.WriteLine(square(5));
// Output:
// 25
</code></pre>
<h3 id="表达式树">表达式树</h3>
<p>这个是<code>LINQ</code>的基础之一,它的作用是将代码像数据一样,保存在内存中;然后稍后对这些“代码数据”进行重新解释/执行。</p>
<p><code>Entity Framework</code>就是一个经典场景,它先将表达式树保存起来,然后执行时,将其翻译为<code>SQL</code>发给数据库执行。</p>
<blockquote>
<p>注意:表达式树并不能表示所有的代码,<code>C# 3.0</code>之后的语法,包含<code>??</code>、<code>?.</code>、<code>async await</code>、可选参数等,都无法放到表达式树中。据说官方准备更新它,但迟迟没有进展。</p>
</blockquote>
<h3 id="扩展方法">扩展方法</h3>
<blockquote>
<p>扩展方法使你能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。</p>
</blockquote>
<pre><code class="language-cs">static void Main()
{
        Console.WriteLine ("Perth".IsCapitalized());       
        // Equivalent to:
        Console.WriteLine (StringHelper.IsCapitalized ("Perth"));       
       
        // Interfaces can be extended, too:
        Console.WriteLine ("Seattle".First());   // S
}

public static class StringHelper
{
        public static bool IsCapitalized (this string s)
        {
                if (string.IsNullOrEmpty(s)) return false;
                return char.IsUpper (s);
        }
       
        public static T First&lt;T&gt; (this IEnumerable&lt;T&gt; sequence)
        {
                foreach (T element in sequence)
                        return element;
               
                throw new InvalidOperationException ("No elements!");
        }
}
</code></pre>
<h3 id="var">var</h3>
<pre><code class="language-cs">var i = 10; // Implicitly typed.
int i = 10; // Explicitly typed.
</code></pre>
<h3 id="分部方法">分部方法</h3>
<pre><code class="language-cs">namespace PM
{
    partial class A
    {
      partial void OnSomethingHappened(string s);
    }

    // This part can be in a separate file.
    partial class A
    {
      // Comment out this method and the program
      // will still compile.
      partial void OnSomethingHappened(String s)
      {
            Console.WriteLine("Something happened: {0}", s);
      }
    }
}
</code></pre>
<h3 id="对象和集合初始值设定项">对象和集合初始值设定项</h3>
<pre><code class="language-cs">public class Cat
{
    // Auto-implemented properties.
    public int Age { get; set; }
    public string Name { get; set; }

    public Cat()
    {
    }

    public Cat(string name)
    {
      this.Name = name;
    }
}
</code></pre>
<h2 id="c-40版---2010">C# 4.0版 - 2010</h2>
<h3 id="dynamic">dynamic</h3>
<p>这个是特性使得<code>CLR</code>不得不进行一次修改。有了这个,<code>C#</code>也能像<code>js</code>、<code>php</code>、<code>python</code>等弱类型语言一样写代码了。</p>
<pre><code class="language-cs">dynamic a = 3;
a = 3.14;
a = "Hello World";
a = new[] { 1, 2, 3, 4, 5 };
a = new Func&lt;int&gt;(() =&gt; 3);
a = new StringBuilder();
Console.WriteLine(a.GetType().Name); // StringBuilder
</code></pre>
<p>注意<code>dynamic</code>可以表示任何东西,包含数组、委托等等。滥用<code>dynamic</code>容易让程序变得很难维护。</p>
<h3 id="命名参数可选参数">命名参数/可选参数</h3>
<pre><code class="language-cs">PrintOrderDetails(productName: "Red Mug", sellerName: "Gift Shop", orderNum: 31);
</code></pre>
<pre><code class="language-cs">public void ExampleMethod(int required, string optionalstr = "default string",
    int optionalint = 10)
</code></pre>
<h3 id="泛型中的协变和逆变">泛型中的协变和逆变</h3>
<pre><code class="language-cs">IEnumerable&lt;Derived&gt; d = new List&lt;Derived&gt;();
IEnumerable&lt;Base&gt; b = d;
</code></pre>
<pre><code class="language-cs">Action&lt;Base&gt; b = (target) =&gt; { Console.WriteLine(target.GetType().Name); };
Action&lt;Derived&gt; d = b;
d(new Derived());
</code></pre>
<h3 id="类型等效内置互操作类型">类型等效、内置互操作类型</h3>
<p>这个主要是为了和<code>COM</code>进行交互。之前需要引用一些<code>COM</code>类型相关的程序集,现在可以直接引用<code>COM</code>。<br>
具体可以参见:https://docs.microsoft.com/zh-cn/dotnet/framework/interop/type-equivalence-and-embedded-interop-types</p>
<h2 id="c-50版---2012">C# 5.0版 - 2012</h2>
<h3 id="asyncawait">async/await</h3>
<pre><code class="language-cs">private DamageResult CalculateDamageDone()
{
    // Code omitted:
    //
    // Does an expensive calculation and returns
    // the result of that calculation.
}

calculateButton.Clicked += async (o, e) =&gt;
{
    // This line will yield control to the UI while CalculateDamageDone()
    // performs its work.The UI thread is free to perform other work.
    var damageResult = await Task.Run(() =&gt; CalculateDamageDone());
    DisplayDamage(damageResult);
};
</code></pre>
<p><code>async</code>/<code>await</code>的本质是状态机,像<code>IEnumerable&lt;T&gt;</code>一样。以前游戏引擎<code>Unity</code>只支持<code>C# 3.0</code>,因此当时它用状态机发<code>Http</code>请求是用的<code>IEnumerable&lt;T&gt;</code>。</p>
<p><code>async</code>/<code>await</code>有两个好处,一是可以避免<code>UI</code>线程卡顿,二是提高系统吞吐率,最终提高性能。</p>
<h3 id="调用方信息">调用方信息</h3>
<pre><code class="language-cs">public void DoProcessing()
{
    TraceMessage("Something happened.");
}

public void TraceMessage(string message,
       string memberName = "",
       string sourceFilePath = "",
       int sourceLineNumber = 0)
{
    System.Diagnostics.Trace.WriteLine("message: " + message);
    System.Diagnostics.Trace.WriteLine("member name: " + memberName);
    System.Diagnostics.Trace.WriteLine("source file path: " + sourceFilePath);
    System.Diagnostics.Trace.WriteLine("source line number: " + sourceLineNumber);
}

// Sample Output:
//message: Something happened.
//member name: DoProcessing
//source file path: c:\Visual Studio Projects\CallerInfoCS\CallerInfoCS\Form1.cs
//source line number: 31
</code></pre>
<p>注意这个是编译期生成的,因此比<code>StackTrace</code>更能保证性能。</p>
<h2 id="c-60版---2015">C# 6.0版 - 2015</h2>
<h3 id="静态导入">静态导入</h3>
<p>终于可以不用写静态类名了。</p>
<pre><code class="language-cs">using static System.Math;
using static System.Console;

WriteLine(Sin(3.14)); // 0.00159265291648683
</code></pre>
<h3 id="异常筛选器">异常筛选器</h3>
<p>在<code>try-catch</code>时,可以按指定的条件进行<code>catch</code>,其它条件不<code>catch</code>。</p>
<pre><code class="language-cs">public static async Task&lt;string&gt; MakeRequest()
{
    WebRequestHandler webRequestHandler = new WebRequestHandler();
    webRequestHandler.AllowAutoRedirect = false;
    using (HttpClient client = new HttpClient(webRequestHandler))
    {
      var stringTask = client.GetStringAsync("https://docs.microsoft.com/en-us/dotnet/about/");
      try
      {
            var responseText = await stringTask;
            return responseText;
      }
      catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("301"))
      {
            return "Site Moved";
      }
    }
}
</code></pre>
<h3 id="自动初始化表达式">自动初始化表达式</h3>
<pre><code class="language-cs">public ICollection&lt;double&gt; Grades { get; } = new List&lt;double&gt;();
</code></pre>
<h3 id="expression-bodied-函数成员">Expression-bodied 函数成员</h3>
<pre><code class="language-cs">public override string ToString() =&gt; $"{LastName}, {FirstName}";
</code></pre>
<h3 id="null传播器">Null传播器</h3>
<pre><code class="language-cs">var first = person?.FirstName;
</code></pre>
<h3 id="字符串内插">字符串内插</h3>
<pre><code class="language-cs">public string GetGradePointPercentage() =&gt;
    $"Name: {LastName}, {FirstName}. G.P.A: {Grades.Average():F2}";
</code></pre>
<h3 id="nameof表达式"><code>nameof</code>表达式</h3>
<p>有时字符串值和某个变量名称一致,尤其是在做参数验证时。这里<code>nameof</code>就能在编译期,自动从变量名生成一个字符串。</p>
<pre><code class="language-cs">if (IsNullOrWhiteSpace(lastName))
    throw new ArgumentException(message: "Cannot be blank", paramName: nameof(lastName));
</code></pre>
<h3 id="索引初始值设定项">索引初始值设定项</h3>
<blockquote>
<p>使集合初始化更容易的另一个功能是对 Add 方法使用扩展方法 。 添加此功能的目的是进行 Visual Basic 的奇偶校验。 如果自定义集合类的方法具有通过语义方式添加新项的名称,则此功能非常有用。</p>
</blockquote>
<h2 id="c-70版本---2017">C# 7.0版本 - 2017</h2>
<h3 id="out变量">out变量</h3>
<pre><code class="language-cs">if (int.TryParse(input, out int result))
    Console.WriteLine(result);
else
    Console.WriteLine("Could not parse input");
</code></pre>
<h3 id="元组和解构">元组和解构</h3>
<pre><code class="language-cs">(string Alpha, string Beta) namedLetters = ("a", "b");
Console.WriteLine($"{namedLetters.Alpha}, {namedLetters.Beta}");
</code></pre>
<p>如上代码所示,解构可以将元组拆分为多个变量。</p>
<h3 id="模式匹配">模式匹配</h3>
<p>现在可以在匹配一个类型时,自动转换为这个类型的变量,如果转换失败,这个变量就赋值为默认值(<code>null</code>或<code>0</code>)。</p>
<p>极简版:</p>
<pre><code class="language-cs">if (input is int count)
    sum += count;
</code></pre>
<p>switch/case版:</p>
<pre><code class="language-cs">public static int SumPositiveNumbers(IEnumerable&lt;object&gt; sequence)
{
    int sum = 0;
    foreach (var i in sequence)
    {
      switch (i)
      {
            case 0:
                break;
            case IEnumerable&lt;int&gt; childSequence:
            {
                foreach(var item in childSequence)
                  sum += (item &gt; 0) ? item : 0;
                break;
            }
            case int n when n &gt; 0:
                sum += n;
                break;
            case null:
                throw new NullReferenceException("Null found in sequence");
            default:
                throw new InvalidOperationException("Unrecognized type");
      }
    }
    return sum;
}
</code></pre>
<h3 id="本地函数">本地函数</h3>
<p>这个主要是方便,<code>javascript</code>就能这样写。</p>
<p>比<code>lambda</code>的好处在于,这个可以定义在后面,而<code>lambda</code>必须定义在前面。</p>
<pre><code class="language-cs">public static IEnumerable&lt;char&gt; AlphabetSubset3(char start, char end)
{
    if (start &lt; 'a' || start &gt; 'z')
      throw new ArgumentOutOfRangeException(paramName: nameof(start), message: "start must be a letter");
    if (end &lt; 'a' || end &gt; 'z')
      throw new ArgumentOutOfRangeException(paramName: nameof(end), message: "end must be a letter");

    if (end &lt;= start)
      throw new ArgumentException($"{nameof(end)} must be greater than {nameof(start)}");

    return alphabetSubsetImplementation();

    IEnumerable&lt;char&gt; alphabetSubsetImplementation()
    {
      for (var c = start; c &lt; end; c++)
            yield return c;
    }
}
</code></pre>
<h3 id="更多的expression-bodied成员">更多的expression-bodied成员</h3>
<p>该功能可以让一些函数写成表达式的形式,非常的方便。</p>
<pre><code class="language-cs">// Expression-bodied constructor
public ExpressionMembersExample(string label) =&gt; this.Label = label;

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

private string label;

// Expression-bodied get / set accessors.
public string Label
{
    get =&gt; label;
    set =&gt; this.label = value ?? "Default label";
}
</code></pre>
<h3 id="ref-局部变量和返回结果">Ref 局部变量和返回结果</h3>
<blockquote>
<p>此功能允许使用并返回对变量的引用的算法,这些变量在其他位置定义。 一个示例是使用大型矩阵并查找具有某些特征的单个位置。</p>
</blockquote>
<p>这个功能主要是为了提高值类型的性能,让它真正发挥其作用。<code>C++</code>就有类似的功能。</p>
<pre><code class="language-cs">public static ref int Find(int[,] matrix, Func&lt;int, bool&gt; predicate)
{
    for (int i = 0; i &lt; matrix.GetLength(0); i++)
      for (int j = 0; j &lt; matrix.GetLength(1); j++)
            if (predicate(matrix))
                return ref matrix;
    throw new InvalidOperationException("Not found");
}
</code></pre>
<pre><code class="language-cs">ref var item = ref MatrixSearch.Find(matrix, (val) =&gt; val == 42);
Console.WriteLine(item);
item = 24;
Console.WriteLine(matrix);
</code></pre>
<h3 id="弃元">弃元</h3>
<blockquote>
<p>通常,在进行元组解构或使用<code>out</code>参数调用方法时,必须定义一个其值无关紧要且你不打算使用的变量。 为处理此情况,<code>C#</code>增添了对弃元的支持 。 弃元是一个名为<code>_</code>的只写变量,可向单个变量赋予要放弃的所有值。 弃元类似于未赋值的变量;不可在代码中使用弃元(赋值语句除外)。</p>
</blockquote>
<pre><code class="language-cs">using System;
using System.Collections.Generic;

public class Example
{
    public static void Main()
    {
      var (_, _, _, pop1, _, pop2) = QueryCityDataForYears("New York City", 1960, 2010);

      Console.WriteLine($"Population change, 1960 to 2010: {pop2 - pop1:N0}");
    }
   
    private static (string, double, int, int, int, int) QueryCityDataForYears(string name, int year1, int year2)
    {
      int population1 = 0, population2 = 0;
      double area = 0;
      
      if (name == "New York City")
      {
            area = 468.48;
            if (year1 == 1960)
            {
                population1 = 7781984;
            }
            if (year2 == 2010)
            {
                population2 = 8175133;
            }
            return (name, area, year1, population1, year2, population2);
      }

      return ("", 0, 0, 0, 0, 0);
    }
}
// The example displays the following output:
//      Population change, 1960 to 2010: 393,149
</code></pre>
<h3 id="二进制文本和数字分隔符">二进制文本和数字分隔符</h3>
<p>这个用于使数字和二进制更可读。</p>
<pre><code class="language-cs">// 二进制文本:
public const int Sixteen =   0b0001_0000;
public const int ThirtyTwo = 0b0010_0000;
public const int SixtyFour = 0b0100_0000;
public const int OneHundredTwentyEight = 0b1000_0000;

// 数字分隔符:
public const long BillionsAndBillions = 100_000_000_000;
public const double AvogadroConstant = 6.022_140_857_747_474e23;
public const decimal GoldenRatio = 1.618_033_988_749_894_848_204_586_834_365_638_117_720_309_179M;
</code></pre>
<h3 id="throw表达式">throw表达式</h3>
<blockquote>
<p><code>throw</code>之前必须是一个语句,因此有时不得不写更多的代码来完成所需功能。但<code>7.0</code>提供了<code>throw</code>表达式来使代码更简洁,阅读更轻松。</p>
</blockquote>
<pre><code class="language-cs">void Main()
{
        // You can now throw expressions in expressions clauses.
        // This is useful in conditional expressions:
       
        string result = new Random().Next(2) == 0 ? "Good" : throw new Exception ("Bad");
        result.Dump();
       
        Foo().Dump();
}

public string Foo() =&gt; throw new NotImplementedException();
</code></pre>
<h2 id="c-80-版---2019">C# 8.0 版 - 2019</h2>
<h3 id="readonly-成员">Readonly 成员</h3>
<pre><code class="language-cs">public readonly override string ToString() =&gt;
    $"({X}, {Y}) is {Distance} from the origin";
</code></pre>
<h3 id="默认接口方法">默认接口方法</h3>
<blockquote>
<p>接口中也能定义方法了,这个新功能经常受到争论。但想想,有时是先定义接口,而实现接口需要实现很多相关、但又繁琐的功能,如<code>ASP.NET Core</code>中的<code>ILogger</code>,谁用谁知道,特别多需要实现的方法,但又都差不多。因此所以这个功能其实很有必要。</p>
</blockquote>
<pre><code class="language-cs">void Main()
{
        ILogger foo = new Logger();
        foo.Log (new Exception ("test"));       
}

class Logger : ILogger
{       
        public void Log (string message) =&gt; Console.WriteLine (message);
}

interface ILogger
{
        void Log (string message);       
       
        // Adding a new member to an interface need not break implementors:
        public void Log (Exception ex) =&gt; Log (ExceptionHeader + ex.Message);
       
        // The static modifier (and other modifiers) are now allowed:
        static string ExceptionHeader = "Exception: ";
}
</code></pre>
<h3 id="模式匹配增强">模式匹配增强</h3>
<p>这个是为简化代码、函数式编程而生的,我个人非常喜欢。</p>
<h4 id="属性模式">属性模式</h4>
<pre><code class="language-cs">public static decimal ComputeSalesTax(Address location, decimal salePrice) =&gt;
    location switch
    {
      { State: "WA" } =&gt; salePrice * 0.06M,
      { State: "MN" } =&gt; salePrice * 0.75M,
      { State: "MI" } =&gt; salePrice * 0.05M,
      // other cases removed for brevity...
      _ =&gt; 0M
    };
</code></pre>
<h4 id="tuple模式">Tuple模式</h4>
<pre><code class="language-cs">public static string RockPaperScissors(string first, string second)
    =&gt; (first, second) switch
    {
      ("rock", "paper") =&gt; "rock is covered by paper. Paper wins.",
      ("rock", "scissors") =&gt; "rock breaks scissors. Rock wins.",
      ("paper", "rock") =&gt; "paper covers rock. Paper wins.",
      ("paper", "scissors") =&gt; "paper is cut by scissors. Scissors wins.",
      ("scissors", "rock") =&gt; "scissors is broken by rock. Rock wins.",
      ("scissors", "paper") =&gt; "scissors cuts paper. Scissors wins.",
      (_, _) =&gt; "tie"
    };
</code></pre>
<h4 id="位置模式">位置模式</h4>
<pre><code class="language-cs">static Quadrant GetQuadrant(Point point) =&gt; point switch
{
    (0, 0) =&gt; Quadrant.Origin,
    var (x, y) when x &gt; 0 &amp;&amp; y &gt; 0 =&gt; Quadrant.One,
    var (x, y) when x &lt; 0 &amp;&amp; y &gt; 0 =&gt; Quadrant.Two,
    var (x, y) when x &lt; 0 &amp;&amp; y &lt; 0 =&gt; Quadrant.Three,
    var (x, y) when x &gt; 0 &amp;&amp; y &lt; 0 =&gt; Quadrant.Four,
    var (_, _) =&gt; Quadrant.OnBorder,
    _ =&gt; Quadrant.Unknown
};
</code></pre>
<h3 id="switch表达式">switch表达式</h3>
<p>这个功能能使代码从大量的<code>if/else</code>或<code>switch/case</code>变成“一行代码”,符合函数式编程的思想,非常好用!</p>
<pre><code class="language-cs">public static RGBColor FromRainbow(Rainbow colorBand) =&gt;
    colorBand switch
    {
      Rainbow.Red    =&gt; new RGBColor(0xFF, 0x00, 0x00),
      Rainbow.Orange =&gt; new RGBColor(0xFF, 0x7F, 0x00),
      Rainbow.Yellow =&gt; new RGBColor(0xFF, 0xFF, 0x00),
      Rainbow.Green=&gt; new RGBColor(0x00, 0xFF, 0x00),
      Rainbow.Blue   =&gt; new RGBColor(0x00, 0x00, 0xFF),
      Rainbow.Indigo =&gt; new RGBColor(0x4B, 0x00, 0x82),
      Rainbow.Violet =&gt; new RGBColor(0x94, 0x00, 0xD3),
      _            =&gt; throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand)),
    };
</code></pre>
<h3 id="using声明">using声明</h3>
<pre><code class="language-cs">static int WriteLinesToFile(IEnumerable&lt;string&gt; lines)
{
    using var file = new System.IO.StreamWriter("WriteLines2.txt");
    // Notice how we declare skippedLines after the using statement.
    int skippedLines = 0;
    foreach (string line in lines)
    {
      if (!line.Contains("Second"))
      {
            file.WriteLine(line);
      }
      else
      {
            skippedLines++;
      }
    }
    // Notice how skippedLines is in scope here.
    return skippedLines;
    // file is disposed here
}
</code></pre>
<h3 id="静态本地函数">静态本地函数</h3>
<p>相比非静态本地函数,静态本地函数没有闭包,因此生成的代码更少,性能也更容易控制。</p>
<pre><code class="language-cs">int M()
{
    int y = 5;
    int x = 7;
    return Add(x, y);

    static int Add(int left, int right) =&gt; left + right;
}
</code></pre>
<h3 id="异步流">异步流</h3>
<p>这个功能和<code>IEnumerable&lt;T&gt;</code>、<code>Task&lt;T&gt;</code>对应,一个经典的表格如下:</p>
<table>
<thead>
<tr>
<th></th>
<th>单值</th>
<th>多值</th>
</tr>
</thead>
<tbody>
<tr>
<td>同步</td>
<td>T</td>
<td>IEnumerable<t></t></td>
</tr>
<tr>
<td>异步</td>
<td>Task<t></t></td>
<td>?</td>
</tr>
</tbody>
</table>
<p>其中,这个问号<code>?</code>终于有了答案,它就叫异步流——<code>IAsyncEnumerable&lt;T&gt;</code>:</p>
<pre><code class="language-cs">public static async System.Collections.Generic.IAsyncEnumerable&lt;int&gt; GenerateSequence()
{
    for (int i = 0; i &lt; 20; i++)
    {
      await Task.Delay(100);
      yield return i;
    }
}
</code></pre>
<p>不像<code>IEnumerable&lt;T&gt;</code>,<code>IAsyncEnumerable&lt;T&gt;</code>系统还没有内置扩展方法,因此可能没有<code>IEnumerable&lt;T&gt;</code>方便,<strong>但是</strong>可以通过安装<code>NuGet</code>包<code>f</code>来实现和<code>IEnumerable&lt;T&gt;</code>一样(或者更爽)的效果。</p>
<h3 id="索引和范围">索引和范围</h3>
<blockquote>
<p>和<code>Python</code>中的切片器一样,只是<code>-</code>用<code>^</code>代替了。</p>
</blockquote>
<pre><code class="language-cs">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

var quickBrownFox = words;
var lazyDog = words[^2..^0];
var allWords = words[..]; // contains "The" through "dog".
var firstPhrase = words[..4]; // contains "The" through "fox"
var lastPhrase = words; // contains "the", "lazy" and "dog"
</code></pre>
<h3 id="null合并赋值">Null合并赋值</h3>
<pre><code class="language-cs">List&lt;int&gt; numbers = null;
int? i = null;

numbers ??= new List&lt;int&gt;();
numbers.Add(i ??= 17);
numbers.Add(i ??= 20);

Console.WriteLine(string.Join(" ", numbers));// output: 17 17
Console.WriteLine(i);// output: 17
</code></pre>
<h3 id="非托管构造类型">非托管构造类型</h3>
<blockquote>
<p>与任何非托管类型一样,可以创建指向此类型的变量的指针,或针对此类型的实例在堆栈上分配内存块</p>
</blockquote>
<pre><code class="language-cs">Span&lt;Coords&lt;int&gt;&gt; coordinates = stackalloc[]
{
    new Coords&lt;int&gt; { X = 0, Y = 0 },
    new Coords&lt;int&gt; { X = 0, Y = 3 },
    new Coords&lt;int&gt; { X = 4, Y = 0 }
};
</code></pre>
<h3 id="嵌套表达式中的-stackalloc">嵌套表达式中的 stackalloc</h3>
<pre><code class="language-cs">Span&lt;int&gt; numbers = stackalloc[] { 1, 2, 3, 4, 5, 6 };
var ind = numbers.IndexOfAny(stackalloc[] { 2, 4, 6 ,8 });
Console.WriteLine(ind);// output: 1
</code></pre>
<h1 id="附录总结">附录/总结</h1>
<p>这么多功能,你印象最深刻的是哪个呢?</p>
<blockquote>
<p>参考资料:C#发展历史 - C#指南 | Microsoft Docs https://docs.microsoft.com/zh-cn/dotnet/csharp/whats-new/csharp-version-history</p>
<p>本文内容和代码由<strong>肖鹏</strong>整理,有修改;转载已获得肖鹏本人授权。<strong>肖鹏</strong>是我公司从<code>Java</code>转<code>.NET</code>的同事。原文链接为:https://akiyax.github.io/new-features-in-csharp/。</p>
</blockquote>
<p>喜欢的朋友请关注我的微信公众号:【DotNet骚操作】</p>
<p><img src="https://img2018.cnblogs.com/blog/233608/201908/233608-20190825165420518-990227633.jpg" alt="DotNet骚操作" loading="lazy"></p><br><br>
来源:https://www.cnblogs.com/sdcb/p/20200228-csharp-history-code-demo.html
頁: [1]
查看完整版本: 代码演示C#各版本新功能