# Learn-CSharp7Plus **Repository Path**: maikebing/Learn-CSharp7Plus ## Basic Information - **Project Name**: Learn-CSharp7Plus - **Description**: 学习C# 7 语法的非常好的例子 - **Primary Language**: C# - **License**: Not specified - **Default Branch**: master - **Homepage**: https://github.com/heku/Learn-CSharp7Plus - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2019-03-24 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # C# 7+ Features ## `out` variables ```csharp int numericResult; if (int.TryParse(input, out numericResult)) Console.WriteLine(numericResult); else Console.WriteLine("Could not parse input"); // Since C# 7.0 if (int.TryParse(input, out int answer)) // or if (int.TryParse(input, out var answer)) Console.WriteLine(answer); else Console.WriteLine("Could not parse input"); ``` ## Tuples ```csharp var tuple = ("a", "b", 3); Console.WriteLine(tuple.Item1); //a Console.WriteLine(tuple.Item2); //b Console.WriteLine(tuple.Item3); //3 (string First, string Last, int Age) tuple = ("a", "b", 3); var tuple = (First: "a", Last: "b", Age: 3); (string First, string Last, int Age) tuple = (First1: "a", Last: "b", Age: 3); //The tuple element name 'First1' is ignored because a different name or no name is specified by the target type '(string First, string Last, int Age)' Console.WriteLine(tuple.First); //a Console.WriteLine(tuple.Last); //b Console.WriteLine(tuple.Age); //3 var name = "heku"; var age = 20; var tuple = (name, age); // C# 7.1 private static (int Max, int Min) Range(IEnumerable numbers) { int min = int.MaxValue; int max = int.MinValue; foreach (var n in numbers) { min = (n < min) ? n : min; max = (n > max) ? n : max; } return (max, min); } var range = Range(Enumerable.Range(1, 100)); // range.Max=100, range.Min=1 (int max,var min) = Range(Enumerable.Range(1, 100)); // max=100, min=1 (var max, _) = Range(Enumerable.Range(1, 100)); // max=100 var (max, min) = Range(Enumerable.Range(1, 100)); // max=100, min=1 var (max, _) = Range(Enumerable.Range(1, 100)); // max=100 var a = (1,2,3); var b = (a:1,b:2,c:3); a == b; // true C# 7.3 ``` - *The new tuples features require the `ValueTuple` types. You must add the NuGet package `System.ValueTuple` in order to use it on platforms that do not include the types.* - *It's perferred for `private` and `internal` methods* ## `Deconstruct` Method ```csharp public class/struct Point { public Point(double x, double y, double z) { this.X = x; this.Y = y; this.Z = z; } public double X { get; } public double Y { get; } public double Z { get; } public void Deconstruct(out double x, out double y) { x = this.X; y = this.Y; } public void Deconstruct(out double x, out double y, out double z) { x = this.X; y = this.Y; z = this.Z; } } var p = new Point(1, 2, 3); var (a, b) = p; // a=1,b=2 var (x, y, z) = p; // x=1,y=2,z=3 public static void Deconstruct(this DateTime date, out int year, out int month, out int day) { year = date.Year; month = date.Month; day = date.Day; } var (year, month, day) = DateTime.Today; ``` ## Discards `_` - `out` parameters - Deconstruction - `is`, `switch` pattern matching ## Pattern Matching ```csharp double? num = xxx; if(num is null){} if(num is 0) {} if(num is double n && n>0) {} if(num is var other){} switch (num) { case 0: Console.WriteLine("zero"); break; case double n when n > 0: Console.WriteLine("positive"); break; case double n when n < 0: Console.WriteLine("negative"); break; case null: Console.WriteLine("num=null"); break; default: Console.WriteLine("Not a number"); break; } ``` - *The order of the `case` expressions now matters* - *The `default` case is always evaluated last* ## Local Functions ```csharp static IEnumerable GetYears(int from, int to) { if (from <= 0) throw new ArgumentOutOfRangeException(nameof(from)); if (to <= 0) throw new ArgumentOutOfRangeException(nameof(to)); if (from > to) throw new ArgumentException(); for (int year = from; year <= to; year++) { yield return year; } } var years = GetYears(0, 10); Console.WriteLine("GetYears Called"); foreach (var year in years) { Console.WriteLine(year); } static IEnumerable GetYears(int from, int to) { if (from <= 0) throw new ArgumentOutOfRangeException(nameof(from)); if (to <= 0) throw new ArgumentOutOfRangeException(nameof(to)); if (from > to) throw new ArgumentException(); return GetYearsInternal(); IEnumerable GetYearsInternal() { for (int year = from; year <= to; year++) { yield return year; } } } // -------------------------------- static async Task GetWebContent(string url) { if (string.IsNullOrWhiteSpace(url)) throw new ArgumentException(nameof(url)); await Task.Delay(2000); // Get content from url return "web content from :" + url; } var task = GetWebContent(null); Console.WriteLine("GetWebContent Called"); var content = await task; Console.WriteLine(content); static Task GetWebContent(string url) { if (string.IsNullOrWhiteSpace(url)) throw new ArgumentException(nameof(url)); return GetWebContentInternal(); async Task GetWebContentInternal() { await Task.Delay(2000); // Get content from url return "web content from :" + url; } } ``` ## More expression-bodied members ```csharp // 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"; } ``` ## Throw expressions ```csharp public string Name { get => name; set => name = value ?? throw new ArgumentNullException(paramName: nameof(value), message: "New name must not be null"); } private ConfigResource loadedConfig = LoadConfigResourceOrDefault() ?? throw new InvalidOperationException("Could not load config"); ``` ## Generalized async return types ```csharp public async ValueTask Func() { await Task.Delay(100); return 5; } ``` ## Numeric literal syntax improvements ```csharp public const int One = 0b0001; public const int Two = 0b0010; public const int Four = 0b0100; public const int Eight = 0b1000; 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; int num = 0x_FFFF; // C# 7.2 int num = 0b_1111_1111_1111_1111; // C# 7.2 ``` ## Async Main (7.1) ```csharp static async Task Main() {...} static async Task Main(string[] args) {...} static async Task Main() {...} static async Task Main(string[] args) {...} ``` ## `default` Literal Expressions (7.1) ```csharp // Before int num = default(int); object obj = default(object); DateTime time = default(DateTime); T Method(CancellationToken token = default(CancellationToken)) { T value = default(T); return default(T); } Method(default(CancellationToken)); // Since C# 7.1 int num = default; object obj = default; DateTime time = default; T Method(CancellationToken token = default) { T value = default; return default; } Method(default); ``` ## `private protected` Access Modifier (7.2) - `private protected` indicates that a member may be accessed by containing class or derived classes that are declared in the same assembly - `protected internal` allows access by derived classes or classes that are in the same assembly ## Non-trailing named arguments (7.2) ```csharp static void Method(string a = "a", string b = "b", string c = "c") {...} // Error: Named argument specifications must appear after all fixed arguments have been specified. Please use language version 7.2 or greater to allow non-trailing named arguments. Method(a:"a", "b", "c"); ``` ## Attach attributes to the backing field for AIP (7.3) ```csharp [field: SomeThingAboutFieldAttribute] public int SomeProperty { get; set; } ``` ## Others - `ref` locals and returns (7.0) - Reference value types (`in`,`ref readonly`,`readonly struct`,`ref struct`) (7.2) - Conditional `ref` expressions (7.2) - You can access `fixed` fields without pinning. (7.3) - You can reassign `ref` local variables. (7.3) - You can use initializers on `stackalloc` arrays. (7.3) - You can use `fixed` statements with any type that supports a pattern. (7.3) - You can use additional generic constraints. (7.3) - You can use expression variables in more locations. (7.3) - Method resolution when arguments differ by `in` has been improved. (7.3) - Overload resolution now has fewer ambiguous cases. (7.3) ## Reference - [What's new in C# 7.0](https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7) - [What's new in C# 7.1](https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7-1) - [What's new in C# 7.2](https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7-2) - [What's new in C# 7.3](https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7-3)