zoukankan      html  css  js  c++  java
  • 代码演示C#各版本新功能

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

    C# 发展历史

    C#各版本新功能其实都能在官网搜到,但很少有人整理在一起,并通过非常简短的代码将每个新特性演示出来。

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

    • C# 2.0版 - 2005

      • 泛型

      • 分部类型

      • 匿名方法

      • 可以为null的值类型

      • 迭代器

      • 协变和逆变

    • C# 3.0版 - 2007

      • 自动实现的属性

      • 匿名类型

      • 查询表达式(LINQ)

      • Lambda表达式

      • 表达式树

      • 扩展方法

      • var

      • 分部方法

      • 对象和集合初始值设定项

    • C# 4.0版 - 2010

      • dynamic

      • 命名参数/可选参数

      • 泛型中的协变和逆变

      • 类型等效、内置互操作类型

    • C# 5.0版 - 2012

      • async/await

      • 调用方信息

    • C# 6.0版 - 2015

      • 静态导入

      • 异常筛选器

      • 自动初始化表达式

      • Expression-bodied 函数成员

      • Null传播器

      • 字符串内插

      • nameof表达式

      • 索引初始值设定项

    • C# 7.0版本 - 2017

      • out变量

      • 元组和析构函数

      • 模式匹配

      • 本地函数

      • 更多的expression-bodied成员

      • Ref 局部变量和返回结果

      • 弃元

      • 二进制文本和数字分隔符

      • throw表达式

    • C# 8.0 版 - 2019

      • Readonly 成员

      • 默认接口方法

      • 模式匹配增强

      • 属性模式

      • Tuple模式

      • 位置模式

      • switch表达式

      • using声明

      • 静态本地函数

      • 异步流

      • 索引和范围

      • Null合并赋值

      • 非托管构造类型

      • 嵌套表达式中的 stackalloc

    • 附录/总结

    C# 2.0版 - 2005

    泛型

    Java中的泛型不支持值类型,且会运行时类型擦除,这一点 .NET更优秀。

    1. // Declare the generic class.

    2. public class GenericList<T>

    3. {

    4. public void Add(T input) { }

    5. }

    6. class TestGenericList

    7. {

    8. private class ExampleClass { }

    9. static void Main()

    10. {

    11. // Declare a list of type int.

    12. GenericList<int> list1 = new GenericList<int>();

    13. list1.Add(1);

    14.  

    15. // Declare a list of type string.

    16. GenericList<string> list2 = new GenericList<string>();

    17. list2.Add("");

    18.  

    19. // Declare a list of type ExampleClass.

    20. GenericList<ExampleClass> list3 = new GenericList<ExampleClass>();

    21. list3.Add(new ExampleClass());

    22. }

    23. }

     

    分部类型

    拆分一个类、一个结构、一个接口或一个方法的定义到两个或更多的文件中是可能的。每个源文件包含类型或方法定义的一部分,编译应用程序时将把所有部分组合起来。

    1. public partial class Employee

    2. {

    3. public void DoWork()

    4. {

    5. }

    6. }

    7.  

    8. public partial class Employee

    9. {

    10. public void GoToLunch()

    11. {

    12. }

    13. }

    匿名方法

    1. Func<int, int, int> sum = delegate (int a, int b) { return a + b; };

    2. Console.WriteLine(sum(3, 4)); // output: 7

    可以为null的值类型

    1. double? pi = 3.14;

    2. char? letter = 'a';

    3.  

    4. int m2 = 10;

    5. int? m = m2;

    6.  

    7. bool? flag = null;

    8.  

    9. // An array of a nullable type:

    10. int?[] arr = new int?[10];

    迭代器

    1. static void Main()

    2. {

    3. foreach (int number in SomeNumbers())

    4. {

    5. Console.Write(number.ToString() + " ");

    6. }

    7. // Output: 3 5 8

    8. Console.ReadKey();

    9. }

    10.  

    11. public static System.Collections.IEnumerable SomeNumbers()

    12. {

    13. yield return 3;

    14. yield return 5;

    15. yield return 8;

    16. }

    协变和逆变

    在 C# 中,协变和逆变能够实现数组类型、委托类型和泛型类型参数的隐式引用转换。协变保留分配兼容性,逆变则与之相反。

    1. // Assignment compatibility.

    2. string str = "test";

    3. // An object of a more derived type is assigned to an object of a less derived type.

    4. object obj = str;

    5.  

    6. // Covariance.

    7. IEnumerable<string> strings = new List<string>();

    8. // An object that is instantiated with a more derived type argument

    9. // is assigned to an object instantiated with a less derived type argument.

    10. // Assignment compatibility is preserved.

    11. IEnumerable<object> objects = strings;

    12.  

    13. // Contravariance.

    14. // Assume that the following method is in the class:

    15. // static void SetObject(object o) { }

    16. Action<object> actObject = SetObject;

    17. // An object that is instantiated with a less derived type argument

    18. // is assigned to an object instantiated with a more derived type argument.

    19. // Assignment compatibility is reversed.

    20. Action<string> actString = actObject;

    C# 3.0版 - 2007

    自动实现的属性

    1. // This class is mutable. Its data can be modified from

    2. // outside the class.

    3. class Customer

    4. {

    5. // Auto-implemented properties for trivial get and set

    6. public double TotalPurchases { get; set; }

    7. public string Name { get; set; }

    8. public int CustomerID { get; set; }

    9.  

    10. // Constructor

    11. public Customer(double purchases, string name, int ID)

    12. {

    13. TotalPurchases = purchases;

    14. Name = name;

    15. CustomerID = ID;

    16. }

    17.  

    18. // Methods

    19. public string GetContactInfo() { return "ContactInfo"; }

    20. public string GetTransactionHistory() { return "History"; }

    21.  

    22. // .. Additional methods, events, etc.

    23. }

    24.  

    25. class Program

    26. {

    27. static void Main()

    28. {

    29. // Intialize a new object.

    30. Customer cust1 = new Customer(4987.63, "Northwind", 90108);

    31.  

    32. // Modify a property.

    33. cust1.TotalPurchases += 499.99;

    34. }

    35. }

    匿名类型

    1. var v = new { Amount = 108, Message = "Hello" };

    2.  

    3. // Rest the mouse pointer over v.Amount and v.Message in the following

    4. // statement to verify that their inferred types are int and n .

    5. Console.WriteLine(v.Amount + v.Message);

    查询表达式(LINQ)

    LINQ允许你可以像写 SQL一样写 C#代码,像这样:

    1. from p in persons

    2. where p.Age > 18 && p.IsBeatiful

    3. select new

    4. {

    5. p.WeChatId,

    6. p.PhoneNumber

    7. }

    LINQ的意义在于让 C#做出了重大调整,本章中说到的 lambda表达式、扩展方法、表达式树、匿名类型、自动属性等,都是 LINQ的必要组成部分。

    由于用扩展方法的形式也能得到一致的结果,而且还能让代码风格更加一致,所以我平时用 LINQ语法较少:

    1. // 与上文代码相同,但改成了扩展方法风格:

    2. persons

    3. .Where(x => x.Age > 18 && x.IsBeatiful)

    4. .Select(x => new

    5. {

    6. x.WeChatId,

    7. x.PhoneNumber,

    8. });

    Lambda表达式

    1. Func<int, int> square = x => x * x;

    2. Console.WriteLine(square(5));

    3. // Output:

    4. // 25

    表达式树

    这个是 LINQ的基础之一,它的作用是将代码像数据一样,保存在内存中;然后稍后对这些“代码数据”进行重新解释/执行。

    EntityFramework就是一个经典场景,它先将表达式树保存起来,然后执行时,将其翻译为 SQL发给数据库执行。

    注意:表达式树并不能表示所有的代码, C# 3.0之后的语法,包含 ??、 ?.、 asyncawait、可选参数等,都无法放到表达式树中。据说官方准备更新它,但迟迟没有进展。

    扩展方法

    扩展方法使你能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。

    1. static void Main()

    2. {

    3. Console.WriteLine ("Perth".IsCapitalized());

    4. // Equivalent to:

    5. Console.WriteLine (StringHelper.IsCapitalized ("Perth"));

    6.  

    7. // Interfaces can be extended, too:

    8. Console.WriteLine ("Seattle".First()); // S

    9. }

    10.  

    11. public static class StringHelper

    12. {

    13. public static bool IsCapitalized (this string s)

    14. {

    15. if (string.IsNullOrEmpty(s)) return false;

    16. return char.IsUpper (s[0]);

    17. }

    18.  

    19. public static T First<T> (this IEnumerable<T> sequence)

    20. {

    21. foreach (T element in sequence)

    22. return element;

    23.  

    24. throw new InvalidOperationException ("No elements!");

    25. }

    26. }

    var

    1. var i = 10; // Implicitly typed.

    2. int i = 10; // Explicitly typed.

    分部方法

    1. namespace PM

    2. {

    3. partial class A

    4. {

    5. partial void OnSomethingHappened(string s);

    6. }

    7.  

    8. // This part can be in a separate file.

    9. partial class A

    10. {

    11. // Comment out this method and the program

    12. // will still compile.

    13. partial void OnSomethingHappened(String s)

    14. {

    15. Console.WriteLine("Something happened: {0}", s);

    16. }

    17. }

    18. }

    对象和集合初始值设定项

    1. public class Cat

    2. {

    3. // Auto-implemented properties.

    4. public int Age { get; set; }

    5. public string Name { get; set; }

    6.  

    7. public Cat()

    8. {

    9. }

    10.  

    11. public Cat(string name)

    12. {

    13. this.Name = name;

    14. }

    15. }

    C# 4.0版 - 2010

    dynamic

    这个是特性使得 CLR不得不进行一次修改。有了这个, C#也能像 js、 php、 python等弱类型语言一样写代码了。

    1. dynamic a = 3;

    2. a = 3.14;

    3. a = "Hello World";

    4. a = new[] { 1, 2, 3, 4, 5 };

    5. a = new Func<int>(() => 3);

    6. a = new StringBuilder();

    7. Console.WriteLine(a.GetType().Name); // StringBuilder

    注意 dynamic可以表示任何东西,包含数组、委托等等。滥用 dynamic容易让程序变得很难维护。

    命名参数/可选参数

    1. PrintOrderDetails(productName: "Red Mug", sellerName: "Gift Shop", orderNum: 31);

    1. public void ExampleMethod(int required, string optionalstr = "default string",

    2. int optionalint = 10)

    泛型中的协变和逆变

    1. IEnumerable<Derived> d = new List<Derived>();

    2. IEnumerable<Base> b = d;

    1. Action<Base> b = (target) => { Console.WriteLine(target.GetType().Name); };

    2. Action<Derived> d = b;

    3. d(new Derived());

    类型等效、内置互操作类型

    这个主要是为了和 COM进行交互。之前需要引用一些 COM类型相关的程序集,现在可以直接引用 COM。具体可以参见:https://docs.microsoft.com/zh-cn/dotnet/framework/interop/type-equivalence-and-embedded-interop-types

    C# 5.0版 - 2012

    async/await

    1. private DamageResult CalculateDamageDone()

    2. {

    3. // Code omitted:

    4. //

    5. // Does an expensive calculation and returns

    6. // the result of that calculation.

    7. }

    8.  

    9. calculateButton.Clicked += async (o, e) =>

    10. {

    11. // This line will yield control to the UI while CalculateDamageDone()

    12. // performs its work. The UI thread is free to perform other work.

    13. var damageResult = await Task.Run(() => CalculateDamageDone());

    14. DisplayDamage(damageResult);

    15. };

    asyncawait的本质是状态机,像 IEnumerable<T>一样。以前游戏引擎 Unity只支持 C# 3.0,因此当时它用状态机发 Http请求是用的 IEnumerable<T>

    asyncawait有两个好处,一是可以避免 UI线程卡顿,二是提高系统吞吐率,最终提高性能。

    调用方信息

    1. public void DoProcessing()

    2. {

    3. TraceMessage("Something happened.");

    4. }

    5.  

    6. public void TraceMessage(string message,

    7. [CallerMemberName] string memberName = "",

    8. [CallerFilePath] string sourceFilePath = "",

    9. [CallerLineNumber] int sourceLineNumber = 0)

    10. {

    11. System.Diagnostics.Trace.WriteLine("message: " + message);

    12. System.Diagnostics.Trace.WriteLine("member name: " + memberName);

    13. System.Diagnostics.Trace.WriteLine("source file path: " + sourceFilePath);

    14. System.Diagnostics.Trace.WriteLine("source line number: " + sourceLineNumber);

    15. }

    16.  

    17. // Sample Output:

    18. // message: Something happened.

    19. // member name: DoProcessing

    20. // source file path: c:Visual Studio ProjectsCallerInfoCSCallerInfoCSForm1.cs

    21. // source line number: 31

    注意这个是编译期生成的,因此比 StackTrace更能保证性能。

    C# 6.0版 - 2015

    静态导入

    终于可以不用写静态类名了。

    1. using static System.Math;

    2. using static System.Console;

    3.  

    4. WriteLine(Sin(3.14)); // 0.00159265291648683

    异常筛选器

    在 try-catch时,可以按指定的条件进行 catch,其它条件不 catch

    1. public static async Task<string> MakeRequest()

    2. {

    3. WebRequestHandler webRequestHandler = new WebRequestHandler();

    4. webRequestHandler.AllowAutoRedirect = false;

    5. using (HttpClient client = new HttpClient(webRequestHandler))

    6. {

    7. var stringTask = client.GetStringAsync("https://docs.microsoft.com/en-us/dotnet/about/");

    8. try

    9. {

    10. var responseText = await stringTask;

    11. return responseText;

    12. }

    13. catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("301"))

    14. {

    15. return "Site Moved";

    16. }

    17. }

    18. }

    自动初始化表达式

    1. public ICollection<double> Grades { get; } = new List<double>();

    Expression-bodied 函数成员

    1. public override string ToString() => $"{LastName}, {FirstName}";

    Null传播器

    1. var first = person?.FirstName;

    字符串内插

    1. public string GetGradePointPercentage() =>

    2. $"Name: {LastName}, {FirstName}. G.P.A: {Grades.Average():F2}";

    nameof表达式

    有时字符串值和某个变量名称一致,尤其是在做参数验证时。这里 nameof就能在编译期,自动从变量名生成一个字符串。

    1. if (IsNullOrWhiteSpace(lastName))

    2. throw new ArgumentException(message: "Cannot be blank", paramName: nameof(lastName));

    索引初始值设定项

    使集合初始化更容易的另一个功能是对 Add 方法使用扩展方法 。添加此功能的目的是进行 Visual Basic 的奇偶校验。如果自定义集合类的方法具有通过语义方式添加新项的名称,则此功能非常有用。

    C# 7.0版本 - 2017

    out变量

    1. if (int.TryParse(input, out int result))

    2. Console.WriteLine(result);

    3. else

    4. Console.WriteLine("Could not parse input");

    元组和解构函数

    1. (string Alpha, string Beta) namedLetters = ("a", "b");

    2. Console.WriteLine($"{namedLetters.Alpha}, {namedLetters.Beta}");

    解构是将元组转换为变量。//忽略…构函数应该类似C++中的析构函数,在实例回收时执行?

    模式匹配

    现在可以在匹配一个类型时,自动转换为这个类型的变量,如果转换失败,这个变量就赋值为默认值( null或 0)。

    极简版:

    1. if (input is int count)

    2. sum += count;

    switch/case版:

    1. public static int SumPositiveNumbers(IEnumerable<object> sequence)

    2. {

    3. int sum = 0;

    4. foreach (var i in sequence)

    5. {

    6. switch (i)

    7. {

    8. case 0:

    9. break;

    10. case IEnumerable<int> childSequence:

    11. {

    12. foreach(var item in childSequence)

    13. sum += (item > 0) ? item : 0;

    14. break;

    15. }

    16. case int n when n > 0:

    17. sum += n;

    18. break;

    19. case null:

    20. throw new NullReferenceException("Null found in sequence");

    21. default:

    22. throw new InvalidOperationException("Unrecognized type");

    23. }

    24. }

    25. return sum;

    26. }

    本地函数

    这个主要是方便, javascript就能这样写。

    比 lambda的好处在于,这个可以定义在后面,而 lambda必须定义在前面。

    1. public static IEnumerable<char> AlphabetSubset3(char start, char end)

    2. {

    3. if (start < 'a' || start > 'z')

    4. throw new ArgumentOutOfRangeException(paramName: nameof(start), message: "start must be a letter");

    5. if (end < 'a' || end > 'z')

    6. throw new ArgumentOutOfRangeException(paramName: nameof(end), message: "end must be a letter");

    7.  

    8. if (end <= start)

    9. throw new ArgumentException($"{nameof(end)} must be greater than {nameof(start)}");

    10.  

    11. return alphabetSubsetImplementation();

    12.  

    13. IEnumerable<char> alphabetSubsetImplementation()

    14. {

    15. for (var c = start; c < end; c++)

    16. yield return c;

    17. }

    18. }

    更多的expression-bodied成员

    该功能可以让一些函数写成表达式的形式,非常的方便。

    1. // Expression-bodied constructor

    2. public ExpressionMembersExample(string label) => this.Label = label;

    3.  

    4. // Expression-bodied finalizer

    5. ~ExpressionMembersExample() => Console.Error.WriteLine("Finalized!");

    6.  

    7. private string label;

    8.  

    9. // Expression-bodied get / set accessors.

    10. public string Label

    11. {

    12. get => label;

    13. set => this.label = value ?? "Default label";

    14. }

    Ref 局部变量和返回结果

    此功能允许使用并返回对变量的引用的算法,这些变量在其他位置定义。一个示例是使用大型矩阵并查找具有某些特征的单个位置。

    这个功能主要是为了提高值类型的性能,让它真正发挥其作用。 C++就有类似的功能。

    1. public static ref int Find(int[,] matrix, Func<int, bool> predicate)

    2. {

    3. for (int i = 0; i < matrix.GetLength(0); i++)

    4. for (int j = 0; j < matrix.GetLength(1); j++)

    5. if (predicate(matrix[i, j]))

    6. return ref matrix[i, j];

    7. throw new InvalidOperationException("Not found");

    8. }

    1. ref var item = ref MatrixSearch.Find(matrix, (val) => val == 42);

    2. Console.WriteLine(item);

    3. item = 24;

    4. Console.WriteLine(matrix[4, 2]);

    弃元

    通常,在进行元组解构或使用 out参数调用方法时,必须定义一个其值无关紧要且你不打算使用的变量。为处理此情况, C#增添了对弃元的支持 。弃元是一个名为 _的只写变量,可向单个变量赋予要放弃的所有值。弃元类似于未赋值的变量;不可在代码中使用弃元(赋值语句除外)。

    1. using System;

    2. using System.Collections.Generic;

    3.  

    4. public class Example

    5. {

    6. public static void Main()

    7. {

    8. var (_, _, _, pop1, _, pop2) = QueryCityDataForYears("New York City", 1960, 2010);

    9.  

    10. Console.WriteLine($"Population change, 1960 to 2010: {pop2 - pop1:N0}");

    11. }

    12.  

    13. private static (string, double, int, int, int, int) QueryCityDataForYears(string name, int year1, int year2)

    14. {

    15. int population1 = 0, population2 = 0;

    16. double area = 0;

    17.  

    18. if (name == "New York City")

    19. {

    20. area = 468.48;

    21. if (year1 == 1960)

    22. {

    23. population1 = 7781984;

    24. }

    25. if (year2 == 2010)

    26. {

    27. population2 = 8175133;

    28. }

    29. return (name, area, year1, population1, year2, population2);

    30. }

    31.  

    32. return ("", 0, 0, 0, 0, 0);

    33. }

    34. }

    35. // The example displays the following output:

    36. // Population change, 1960 to 2010: 393,149

    二进制文本和数字分隔符

    这个用于使数字和二进制更可读。

    1. // 二进制文本:

    2. public const int Sixteen = 0b0001_0000;

    3. public const int ThirtyTwo = 0b0010_0000;

    4. public const int SixtyFour = 0b0100_0000;

    5. public const int OneHundredTwentyEight = 0b1000_0000;

    6.  

    7. // 数字分隔符:

    8. public const long BillionsAndBillions = 100_000_000_000;

    9. public const double AvogadroConstant = 6.022_140_857_747_474e23;

    10. public const decimal GoldenRatio = 1.618_033_988_749_894_848_204_586_834_365_638_117_720_309_179M;

    throw表达式

    throw之前必须是一个语句,因此有时不得不写更多的代码来完成所需功能。但 7.0提供了 throw表达式来使代码更简洁,阅读更轻松。

    1. void Main()

    2. {

    3. // You can now throw expressions in expressions clauses.

    4. // This is useful in conditional expressions:

    5.  

    6. string result = new Random().Next(2) == 0 ? "Good" : throw new Exception ("Bad");

    7. result.Dump();

    8.  

    9. Foo().Dump();

    10. }

    11.  

    12. public string Foo() => throw new NotImplementedException();

    C# 8.0 版 - 2019

    Readonly 成员

    1. public readonly override string ToString() =>

    2. $"({X}, {Y}) is {Distance} from the origin";

    默认接口方法

    接口中也能定义方法了,这个新功能经常受到争论。但想想,有时是先定义接口,而实现接口需要实现很多相关、但又繁琐的功能,如 ASP.NETCore中的 ILogger,谁用谁知道,特别多需要实现的方法,但又都差不多。因此所以这个功能其实很有必要。

    1. void Main()

    2. {

    3. ILogger foo = new Logger();

    4. foo.Log (new Exception ("test"));

    5. }

    6.  

    7. class Logger : ILogger

    8. {

    9. public void Log (string message) => Console.WriteLine (message);

    10. }

    11.  

    12. interface ILogger

    13. {

    14. void Log (string message);

    15.  

    16. // Adding a new member to an interface need not break implementors:

    17. public void Log (Exception ex) => Log (ExceptionHeader + ex.Message);

    18.  

    19. // The static modifier (and other modifiers) are now allowed:

    20. static string ExceptionHeader = "Exception: ";

    21. }

    模式匹配增强

    这个是为简化代码、函数式编程而生的,我个人非常喜欢。

    属性模式

    1. public static decimal ComputeSalesTax(Address location, decimal salePrice) =>

    2. location switch

    3. {

    4. { State: "WA" } => salePrice * 0.06M,

    5. { State: "MN" } => salePrice * 0.75M,

    6. { State: "MI" } => salePrice * 0.05M,

    7. // other cases removed for brevity...

    8. _ => 0M

    9. };

    Tuple模式

    1. public static string RockPaperScissors(string first, string second)

    2. => (first, second) switch

    3. {

    4. ("rock", "paper") => "rock is covered by paper. Paper wins.",

    5. ("rock", "scissors") => "rock breaks scissors. Rock wins.",

    6. ("paper", "rock") => "paper covers rock. Paper wins.",

    7. ("paper", "scissors") => "paper is cut by scissors. Scissors wins.",

    8. ("scissors", "rock") => "scissors is broken by rock. Rock wins.",

    9. ("scissors", "paper") => "scissors cuts paper. Scissors wins.",

    10. (_, _) => "tie"

    11. };

    位置模式

    1. static Quadrant GetQuadrant(Point point) => point switch

    2. {

    3. (0, 0) => Quadrant.Origin,

    4. var (x, y) when x > 0 && y > 0 => Quadrant.One,

    5. var (x, y) when x < 0 && y > 0 => Quadrant.Two,

    6. var (x, y) when x < 0 && y < 0 => Quadrant.Three,

    7. var (x, y) when x > 0 && y < 0 => Quadrant.Four,

    8. var (_, _) => Quadrant.OnBorder,

    9. _ => Quadrant.Unknown

    10. };

    switch表达式

    这个功能能使代码从大量的 if/else或 switch/case变成“一行代码”,符合函数式编程的思想,非常好用!

    1. public static RGBColor FromRainbow(Rainbow colorBand) =>

    2. colorBand switch

    3. {

    4. Rainbow.Red => new RGBColor(0xFF, 0x00, 0x00),

    5. Rainbow.Orange => new RGBColor(0xFF, 0x7F, 0x00),

    6. Rainbow.Yellow => new RGBColor(0xFF, 0xFF, 0x00),

    7. Rainbow.Green => new RGBColor(0x00, 0xFF, 0x00),

    8. Rainbow.Blue => new RGBColor(0x00, 0x00, 0xFF),

    9. Rainbow.Indigo => new RGBColor(0x4B, 0x00, 0x82),

    10. Rainbow.Violet => new RGBColor(0x94, 0x00, 0xD3),

    11. _ => throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand)),

    12. };

    using声明

    1. static int WriteLinesToFile(IEnumerable<string> lines)

    2. {

    3. using var file = new System.IO.StreamWriter("WriteLines2.txt");

    4. // Notice how we declare skippedLines after the using statement.

    5. int skippedLines = 0;

    6. foreach (string line in lines)

    7. {

    8. if (!line.Contains("Second"))

    9. {

    10. file.WriteLine(line);

    11. }

    12. else

    13. {

    14. skippedLines++;

    15. }

    16. }

    17. // Notice how skippedLines is in scope here.

    18. return skippedLines;

    19. // file is disposed here

    20. }

    静态本地函数

    相比非静态本地函数,静态本地函数没有闭包,因此生成的代码更少,性能也更容易控制。

    1. int M()

    2. {

    3. int y = 5;

    4. int x = 7;

    5. return Add(x, y);

    6.  

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

    8. }

    异步流

    这个功能和 IEnumerable<T>、 Task<T>对应,一个经典的表格如下:

     单值多值
    同步 T IEnumerable
    异步 Task ?

    其中,这个问号 ?终于有了答案,它就叫异步流—— IAsyncEnumerable<T>

    1. public static async System.Collections.Generic.IAsyncEnumerable<int> GenerateSequence()

    2. {

    3. for (int i = 0; i < 20; i++)

    4. {

    5. await Task.Delay(100);

    6. yield return i;

    7. }

    8. }

    不像 IEnumerable<T>, IAsyncEnumerable<T>系统还没有内置扩展方法,因此可能没有 IEnumerable<T>方便,但是可以通过安装 NuGet包 f来实现和 IEnumerable<T>一样(或者更爽)的效果。

    索引和范围

    和 Python中的切片器一样,只是 -用 ^代替了。

    1. var words = new string[]

    2. {

    3. // index from start index from end

    4. "The", // 0 ^9

    5. "quick", // 1 ^8

    6. "brown", // 2 ^7

    7. "fox", // 3 ^6

    8. "jumped", // 4 ^5

    9. "over", // 5 ^4

    10. "the", // 6 ^3

    11. "lazy", // 7 ^2

    12. "dog" // 8 ^1

    13. }; // 9 (or words.Length) ^0

    14.  

    15. var quickBrownFox = words[1..4];

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

    17. var allWords = words[..]; // contains "The" through "dog".

    18. var firstPhrase = words[..4]; // contains "The" through "fox"

    19. var lastPhrase = words[6..]; // contains "the", "lazy" and "dog"

    Null合并赋值

    1. List<int> numbers = null;

    2. int? i = null;

    3.  

    4. numbers ??= new List<int>();

    5. numbers.Add(i ??= 17);

    6. numbers.Add(i ??= 20);

    7.  

    8. Console.WriteLine(string.Join(" ", numbers)); // output: 17 17

    9. Console.WriteLine(i); // output: 17

    非托管构造类型

    与任何非托管类型一样,可以创建指向此类型的变量的指针,或针对此类型的实例在堆栈上分配内存块

    1. Span<Coords<int>> coordinates = stackalloc[]

    2. {

    3. new Coords<int> { X = 0, Y = 0 },

    4. new Coords<int> { X = 0, Y = 3 },

    5. new Coords<int> { X = 4, Y = 0 }

    6. };

    嵌套表达式中的 stackalloc

    1. Span<int> numbers = stackalloc[] { 1, 2, 3, 4, 5, 6 };

    2. var ind = numbers.IndexOfAny(stackalloc[] { 2, 4, 6 ,8 });

    3. Console.WriteLine(ind); // output: 1

    附录/总结

    这么多功能,你印象最深刻的是哪个呢?

  • 相关阅读:
    windows 设置nginx开机自启动
    vue js中解决二进制转图片显示问题
    oracle 各种问题
    Nginx安装及配置详解包括windows linux 环境
    AOP-切面是如何织入到目标对象中的
    AOP-通知-笔记
    AOP-方法拦截器-笔记
    JdkDynamicAopProxy-笔记
    Joinpoint继承体系-笔记
    AOP-Pointcut-笔记
  • 原文地址:https://www.cnblogs.com/qiyebao/p/12388290.html
Copyright © 2011-2022 走看看