What is new in C# 7,8,9,10

目录

[What's new in C# 7](# 7)

[C# 7 in Visual Studio 2017](# 7 in Visual Studio 2017)

[Out Variables](#Out Variables)

[Pattern Matching](#Pattern Matching)

[Tuples (System.ValueTuple)](#Tuples (System.ValueTuple))

Deconstruct解构

[Local Functions](#Local Functions)

[Ref Returns and Locals](#Ref Returns and Locals)

[Expression Bodied Members](#Expression Bodied Members)

[Throw Expressions](#Throw Expressions)

[Generated Async Return Types](#Generated Async Return Types)

[Literal Improvements](#Literal Improvements)

[C# 7.1 in Visual Studio 2017.3](# 7.1 in Visual Studio 2017.3)

Compilation Issues (how to switch to C#7.1))

[Async Main](#Async Main)

[Default Expressions](#Default Expressions)

[Ref Assemblies](#Ref Assemblies)

[Infer Tuple Names](#Infer Tuple Names)

[Pattern-Matching with Generics](#Pattern-Matching with Generics)

[C# 7.2 in Visual Studio 2017 15.5](# 7.2 in Visual Studio 2017 15.5)

[Leading Digit Separators](#Leading Digit Separators)

['Private Protected' Access Modifier](#'Private Protected' Access Modifier)

[Non-Trailing Named Arguments](#Non-Trailing Named Arguments)

[Reference Semantics on Value Types值类型的引用语义](#Reference Semantics on Value Types值类型的引用语义)

[C# 7.3 in Visual Studio 2017 15.5](# 7.3 in Visual Studio 2017 15.5)

[Performance Improvement](#Performance Improvement)

[Features Enhancements](#Features Enhancements)

[Extensioned expression variables in initializers](#Extensioned expression variables in initializers)

[New Compiler Features](#New Compiler Features)

[What's new in C# 8](# 8)

[Nullable Reference Types](#Nullable Reference Types)

[Method 1: 通过引用ReSharper.Annotations](#Method 1: 通过引用ReSharper.Annotations)

[Method 2: 通过使用string?](#Method 2: 通过使用string?)

[Override Null Checks](#Override Null Checks)

[Diable nullable check](#Diable nullable check)

[Enable nullable check](#Enable nullable check)

[Index and Range](#Index and Range)

[Default Interface Members](#Default Interface Members)

[Extension to create Default function in Interface](#Extension to create Default function in Interface)

[Default function in iterface](#Default function in iterface)

[Pattern Matching](#Pattern Matching)

[What's new in C# 9 (. NET 5)](# 9 (. NET 5))

[Record Types](#Record Types)

浅拷贝with

[Top-level Calls](#Top-level Calls)

[Initial Setters](#Initial Setters)

[Pttern Matching Improvement](#Pttern Matching Improvement)

[Target-Typed New](#Target-Typed New)

[Source Generators](#Source Generators)

[Partial Method Syntax and Modules Initializers(部分方法语法和模块初始化器)](#Partial Method Syntax and Modules Initializers(部分方法语法和模块初始化器))

[What's new in C# 10 (. NET 6)](# 10 (. NET 6))

[Record Structs](#Record Structs)

[Global Using Directives](#Global Using Directives)

[File-Scoped Namespace Declarations](#File-Scoped Namespace Declarations)

[Extended Property Patterns](#Extended Property Patterns)

[Generic Attributes通用属性](#Generic Attributes通用属性)

[Lambda Improvements,提高很多](#Lambda Improvements,提高很多)

[Enhanced #line directives](#line directives)



What's new in C# 7

C# 7 in Visual Studio 2017

Out Variables

cs 复制代码
using System;
using static System.Console;

namespace CSharp7Demos
{
  class OutVariables
  {
    static void MainOV(string[] args)
    {
      DateTime dt; // struct
      if (DateTime.TryParse("01/01/2017", out dt))
      {
        WriteLine($"Old-fashioned parse: {dt}");
      }

      // variable declaration is an expression, not a statement
      if (DateTime.TryParse("02/02/2016", out /*DateTime*/ var dt2))
      {
        WriteLine($"New parse: {dt2}");
      }

      // the scope of dt2 extends outside the if block
      WriteLine($"I can use dt2 here: {dt2}");

      // what if the parse fails?
      int.TryParse("abc", out var i);
      WriteLine($"i = {i}"); // default value
    }
  }
}

Pattern Matching

cs 复制代码
using static System.Console;

namespace CSharp7Demos
{
  public class Shape
  {
    
  }
  

  public class Rectangle : Shape
  {
    public int Width, Height;
  }

  public class Circle : Shape
  {
    public int Diameter;
  }

  public class PatternMatching
  {
    public void DisplayShape(Shape shape)
    {
      if (shape is Rectangle)
      {
        var rc = (Rectangle) shape;

      } else if (shape is Circle)
      {
        // ...
      }


      var rect = shape as Rectangle;
      if (rect != null) // nonnull
      {
        //...
      }

      if (shape is Rectangle r)
      {
        // use r
      }

      // can also do the invserse
      if (!(shape is Circle cc))
      {
        // not a circle!
      }


      switch (shape)
      {
        case Circle c:
          // use c
          break;
        case Rectangle sq when (sq.Width == sq.Height):
          // square!
          break;
        case Rectangle rr:
          // use rr
          break;
      }

      var z = (23, 32);

      //switch (z)
      //{
      //  case (0, 0):
      //    WriteLine("origin");
      //}
    }

    static void Main(string[] args)
    {
      
    }
  }
}

Tuples (System.ValueTuple)

cs 复制代码
using System;
using System.Linq;
using Microsoft.SqlServer.Server;
using static System.Console;

namespace CSharp7Demos
{
  public class Point
  {
    public int X, Y;

    public void Deconstruct(out string s)
    {
      s = $"{X}-{Y}";
    }

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

  public class Tuples
  {
    static Tuple<double, double> SumAndProduct(double a, double b)
    {
      return Tuple.Create(a + b, a * b);
    }

    // requires ValueTuple nuget package
    // originally with no names
    static (double sum, double product) NewSumAndProduct(double a, double b)
    {
      return (a+b,a*b);
    }

    static void MainT(string[] args)
    {
      // New
      var sp = SumAndProduct(2, 5);
      // sp.Item1 ugly
      WriteLine($"sum = {sp.Item1}, product = {sp.Item2}");

      var sp2 = NewSumAndProduct(2, 5);
      WriteLine($"new sum = {sp2.sum}, product = {sp2.product}");
      WriteLine($"Item1 = {sp2.Item1}");
      WriteLine(sp2.GetType());

      // converting to valuetuple loses all info
      var vt = sp2;
      // back to Item1, Item2, etc...
      var item1 = vt.Item1; // :(

      // can use var below
      //(double sum, var product) = NewSumAndProduct(3, 5);
      var (sum, product) = NewSumAndProduct(3, 5);

      // note! var works but double doesn't
      // double (s, p) = NewSumAndProduct(3, 4);
      (double s, double p) = NewSumAndProduct(3, 4);//This can work
      WriteLine($"sum = {sum}, product = {product}");
      WriteLine(sum.GetType());

      // also assignment
      double s, p;
      (s, p) = NewSumAndProduct(1, 10);

      // tuple declarations with names
      //var me = new {name = "Evan", age = 37}; // AnonymousType
      var me = (name: "Evan", age: 37);
      WriteLine(me);
      WriteLine(me.GetType());//Print is System.ValueTuple

      // names are not part of the type:
      WriteLine("Fields: " + string.Join(",", me.GetType().GetFields().Select(pr => pr.Name)));
      WriteLine("Properties: " + string.Join(",", me.GetType().GetProperties().Select(pr => pr.Name)));

      WriteLine($"My name is {me.name} and I am {me.age} years old");
      // proceed to show return: TupleElementNames in dotPeek (internally, Item1 etc. are used everywhere)

      // unfortunately, tuple names only propagate out of a function if they're in the signature
      var snp = new Func<double, double, (double sum, double product)>((a, b) => (sum: a + b, product: a * b));
      var result = snp(1, 2);
      // there's no result.sum unless you add it to signature
      WriteLine($"sum = {result.sum}");
      
     
    }
  }
}

Deconstruct解构

cs 复制代码
// deconstruction
Point pt = new Point {X = 2, Y = 3};
var (x,y) = pt; // interesting error here
Console.WriteLine($"Got a point x = {x}, y = {y}");
      
// can also discard values
(int z, _) = pt;

Local Functions

CalculateDiscriminant可以放在方法SolveQuadratic体内,或者方法体外,放在方法体内是,所在的位置可以在调用前或者调用后面。

建议放在前面,方便代码维护。

cs 复制代码
  public class Employee
  {
      public int Id { get; set; }

      public string FirstName { get; set; }
      public string LastName { get; set; }
      public string MiddleName { get; set; }
       //normal ctor
      //public Employee(string firstName, string lastName, string middleName)
      //{
      //    FirstName = firstName;
      //    LastName = lastName;
      //    MiddleName = middleName;
      //}
        //lambda ctor
      public Employee(string firstName, string lastName, string middleName) => (FirstName, LastName, MiddleName) = (firstName, lastName, middleName);

  }
cs 复制代码
using System;

namespace CSharpDemos
{
  public class EquationSolver
  {
    //private Func<double, double, double, double> CalculateDiscriminant = (aa, bb, cc) => bb * bb - 4 * aa * cc;
    
    //Quadratic 二次方程
    public static Tuple<double, double> SolveQuadratic(double a, double b, double c)
    {
      //var CalculateDiscriminant = new Func<double, double, double, double>((aa, bb, cc) => bb * bb - 4 * aa * cc);

      //double CalculateDiscriminant(double aa, double bb, double cc)
      //{
      //  return bb * bb - 4 * aa * cc;
      //}
      //double CalculateDiscriminant(double aa, double bb, double cc) => bb * bb - 4 * aa * cc;
      //double CalculateDiscriminant() => b * b - 4 * a * c;

      //var disc = CalculateDiscriminant(a, b, c);
      var disc = CalculateDiscriminant();

      var rootDisc = Math.Sqrt(disc);
      return Tuple.Create(
        (-b + rootDisc) / (2 * a),
        (-b - rootDisc) / (2 * a)
      );

      // can place here
      double CalculateDiscriminant() => b * b - 4 * a * c;
    }

    //private static double CalculateDiscriminant(double a, double b, double c)
    //{
    //  return b * b - 4 * a * c;
    //}
  }

  public class LocalFunctions
  {
    static void MainT(string[] args)
    {
      var result = EquationSolver.SolveQuadratic(1, 10, 16);
      Console.WriteLine(result);
    }
  }
}

Ref Returns and Locals

cs 复制代码
using System;
using System.Collections.Generic;
using static System.Console;

namespace CSharpDemos
{
  public class RefReturnsAndLocals
  {
    static ref int Find(int[] numbers, int value)
    {
      for (int i = 0; i < numbers.Length; i++)
      {
        if (numbers[i] == value)
          return ref numbers[i];
      }

      // cannot do by value return
      //return -1;

      // cannot return a local
      //int fail = -1;
      //return ref fail;

      throw new ArgumentException("meh");
    }

    static ref int Min(ref int x, ref int y)
    {
      //return x < y ? (ref x) : (ref) y;
      //return ref (x < y ? x : y);
      if (x < y) return ref x;
      return ref y;
    }

    static void MainRRL(string[] args)
    {
      // reference to a local element
      int[] numbers = { 1, 2, 3 };
      ref int refToSecond = ref numbers[1];
      var valueOfSecond = refToSecond;

      // cannot rebind
      // refToSecond = ref numbers[0];

      refToSecond = 123;
      WriteLine(string.Join(",", numbers)); // 1, 123, 3

      // reference persists even after the array is resized
      Array.Resize(ref numbers, 1);
      WriteLine($"second = {refToSecond}, array size is {numbers.Length}");
      refToSecond = 321;
      WriteLine($"second = {refToSecond}, array size is {numbers.Length}");
      //numbers.SetValue(321, 1); // will throw

      // won't work with lists
      var numberList = new List<int> {1, 2, 3};
      //ref int second = ref numberList[1]; // property or indexer cannot be out


      int[] moreNumbers = {10, 20, 30};
      //ref int refToThirty = ref Find(moreNumbers, 30);
      //refToThirty = 1000;

      // disgusting use of language
      Find(moreNumbers, 30) = 1000;

      WriteLine(string.Join(",",moreNumbers));

      // too many references:
      int a = 1, b = 2;
      ref var minRef = ref Min(ref a, ref b);

      // non-ref call just gets the value
      int minValue = Min(ref a, ref b);
      WriteLine($"min is {minValue}");
    }
  }
}

Expression Bodied Members

cs 复制代码
using System.Collections.Generic;

namespace CSharpDemos
{
  // community contributed feature
  public class Person
  {
    private int id;

    private static readonly Dictionary<int, string> names = new Dictionary<int, string>();

    public Person(int id, string name) => names.Add(id, name);
    ~Person() => names.Remove(id);

    public string Name
    {
      get => names[id];
      set => names[id] = value;
    }
  }
}

Throw Expressions

cs 复制代码
using System;
using static System.Console;

namespace CSharpDemos
{
  public class ThrowExpressions
  {
    public string Name { get; set; }

    public ThrowExpressions(string name)
    {
      Name = name ?? throw new ArgumentNullException(paramName: nameof(name));
    }

    int GetValue(int n)
    {
      return n > 0 ? n + 1 : throw new Exception();
    }

    static void MainTE(string[] args)
    {
      int v = -1;
      try
      {
        var te = new ThrowExpressions("");
        v = te.GetValue(-1); // does not get defaulted!
      }
      catch (Exception e)
      {
        Console.WriteLine(e);
      }
      finally
      {
        WriteLine(v);
      }
    }
  }
}

Generated Async Return Types

cs 复制代码
using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;

namespace CSharpDemos
{
  public class GeneralizedAsyncReturnTypes
  {
    public static async Task<long> GetDirSize(string dir)
    {
      if (!Directory.EnumerateFileSystemEntries(dir).Any())
        return 0;

      // Task<long> is return type so it still needs to be instantiated

      return await Task.Run(() => Directory.GetFiles(dir, "*.*", SearchOption.AllDirectories)
        .Sum(f => new FileInfo(f).Length));
    }

    // C# 7 lets us define custom return types on async methods
    // main requirement is to implement GetAwaiter() method

    // ValueTask is a good example

      // need nuget package
    public static async ValueTask<long> NewGetDirSize(string dir)
    {
      if (!Directory.EnumerateFileSystemEntries(dir).Any())
        return 0;

      // Task<long> is return type so it still needs to be instantiated

      return await Task.Run(() => Directory.GetFiles(dir, "*.*", SearchOption.AllDirectories)
        .Sum(f => new FileInfo(f).Length));
    }

    static void MainGART(string[] args)
    {
      // async methods used to require void, Task or Task<T>

      // C# 7 allows other types such as ValueType<T> - prevent
      // allocation of a task when the result is already available
      // at the time of awaiting

      Console.WriteLine(NewGetDirSize(@"c:\temp").Result);
    }
  }
}

Literal Improvements

cs 复制代码
namespace CSharpDemos
{
  public class LiteralImprovements
  {
    static void MainLI(string[] args)
    {
      int a = 123_321;
      int b = 123_321______123;

      // cannot do trailing
      //int c = 1_2_3___; // R# remove

      // also works for hex
      long h = 0xAB_BC_D123EF;

      // also binay
      var bin = 0b1110_0010_0011;
    }
  }
}

C# 7.1 in Visual Studio 2017.3

Compilation Issues (how to switch to C#7.1)

以下内码在vs2017.3中会报编译错误,你可以从Solution Explore右击Project-->Properties-->Build --> Advance去更改C#版本到7.1去解决编译错误。

cs 复制代码
static async Task Main(string[] args)
    {

      Console.WriteLine("ABC");
    }

Async Main

cs 复制代码
using System;
using System.Net.Http;
using System.Threading.Tasks;

namespace CSharpDemos
{
  internal class Program
  {
    // used to be the case that your demo
    // would have to reside in a separate
    // body

    private static string url = "http://google.com/robots.txt";

    //private static async Task MainAsync(string s)
    //{
    //  // blah
    //  Console.WriteLine(await new HttpClient().GetStringAsync(s));
    //}

    //public static void Main(string[] args)
    //{
    //  // fine
    //  MainAsync(url).GetAwaiter().GetResult();
    //}

    
    // there is no async void, it's

    // Task Main
    // Task<int> Main if you need to return
    static async Task Main(string[] args)
    {
      Console.WriteLine(await new HttpClient().GetStringAsync(url));
    }
  }
}

Default Expressions

cs 复制代码
using System;
using System.Collections.Generic;
using static System.Console;

namespace CSharpDemos
{
  public class DefaultLiteral
  {
    // allowed in argument names
    // only upside: switching from ref to value type

    // VS Action 'Simplify Default Expression'
    public DateTime GetTimestamps(List<int> items = default(List<int>))
    {
      // ...
      return default;
    }

    /// <summary>
    /// Default literal, one of the slightly meaningless features.
    /// </summary>
    static void Main()
    {
      // Simplify default expression here
      int a = default(int);
      WriteLine(a);
      int av = default;//same as above, 0 is int default value
      WriteLine(av);
 
      int b = default;
      WriteLine(b);

      // constants are ok if the inferred type is suitable
      const int c = default;
      WriteLine(c);

      // will not work here
      // const int? d = default; // oops

      // cannot leave defaults on their own
      var e = new[] {default, 33, default};
      WriteLine(string.Join(",", e));

      // rather silly way of doing things; null is shorter
      string s = default;
      WriteLine(s == null);

      // comparison with default is OK if type can be inferred
      if (s == default)
      {
        WriteLine("Yes, s is default/null");
      }

      // ternary operations
      var x = a > 0 ? default : 1.5;
      WriteLine(x.GetType().Name);
    }
  }
}

Ref Assemblies

利用Refelection反编译后,代码实现部分只有null返回,其他部分被隐藏了。

Infer Tuple Names

cs 复制代码
using System;
using System.Linq;

namespace CSharpDemos
{
  using static System.Console;

  public class InferTupleNames
  {
    // Tuple projection initializers
    public static void Main(string[] args)
    {
      // reminder: tuples
      var me = (name: "Evan", age: 37);
      WriteLine(me);

      var alsoMe = (me.age, me.name);
      WriteLine(alsoMe.Item1); // typical
      WriteLine(alsoMe.name); // new

      var result = new[] {"March", "April", "May"} // explicit name not required
        .Select(m => (
          /*Length:*/ m/*?*/.Length, // optionally nullable
          FirstChar: m[0])) // some names (e.g., ToString) disallowed
        .Where(t => t.Length == 5); // note how .Length is available here
      WriteLine(string.Join(",", result));

      // tuples produced by deconstruction
      var now = DateTime.UtcNow;
      var u = (now.Hour, now.Minute);
      var v = ((u.Hour, u.Minute) = (11, 12));
      WriteLine(v.Minute);
    }
  }
}

Pattern-Matching with Generics

cs 复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharpDemos
{
  using static System.Console;

  public class Animal
  {

  }

  public class Pig : Animal
  {

  }

  public class PatternMatchingWithGenerics
  {
    public static void Cook<T>(T animal)
      where T : Animal
    {
      // note the red squiggly!
      // cast is redundant here
      if ((object)animal is Pig pig)
      {
        // cook and eat it
        Write("We cooked and ate the pig...");
      }

      switch (/*(object)*/animal)
      {
        case Pig pork:
          WriteLine(" and it tastes delicious!");
          break;
      }
    }

    /// <summary>
    /// Need to fall back to C# 7 for this.
    /// </summary>
    static void Main(string[] args)
    {
      var pig = new Pig();
      Cook(pig);
    }
  }
}

C# 7.2 in Visual Studio 2017 15.5

Leading Digit Separators

cs 复制代码
  class LeadingUnderscoresNumericSeparators
  {
    static void Main(string[] args)
    {
      // binary
      var x = 0b_1111_0000;

      // hex
      var y = 0x_baad_f00d;
    }
  }

'Private Protected' Access Modifier

cs 复制代码
public class Base
  {
    private int a;
    protected internal int b; // derived classes or classes in same assembly
    private protected int c;  // containing class or derived classes in same assembly only 
  }

  class Derived : Base
  {
    public Derived()
    {
      c = 333; // fine

      b = 3; // no
    }
  }

  class Foo
  {
    static void Main(string[] args)
    {
      Base pp = new Base();
      var d = new Derived();
      d.b = 3;
      // d.c is a no-go
    }
  }

Non-Trailing Named Arguments

cs 复制代码
static void doSomething(int foo, int bar)
    {

    }

    static void Main(string[] args)
    {
      doSomething(foo: 33, 44);

      // still illegal
      //doSomething(33, foo:44)
    }

Reference Semantics on Value Types值类型的引用语义

'In' Parameters

'Ref Readonly' Variables

'Ref Struct' and Span

cs 复制代码
struct Point
  {
    public double X, Y;

    public Point(double x, double y)
    {
      X = x;
      Y = y;
    }

    public void Reset()
    {
      X = Y = 0;
    }

    // we don't want to recreate origin as new Point(), so...
    private static Point origin = new Point();
    public static ref readonly Point Origin => ref origin;

    public override string ToString()
    {
      return $"({X},{Y})";
    }
  }

  public class RefSemanticsValueTypes
  {
    // IN PARAMETERS

    void changeMe(ref Point p)
    {
      p.X++;
    }

    // structs are passed by reference (i.e. address, so 32 or 64 bits)
    // 'in' is effectively by-ref and read-only
    double MeasureDistance(in Point p1, in Point p2)
    {
      // cannot assign to in parameter
      // p1 = new Point();

      // cannot pass as ref or out method
      // obvious
      // changeMe(ref p2);

      p2.Reset(); // instance operations happen on a copy!

      var dx = p1.X - p2.X;
      var dy = p1.Y - p2.Y;
      return Math.Sqrt(dx * dx + dy * dy);
    }

    // cannot create overloads that differ only in presence?
    // yeah you can, but
    //double MeasureDistance(Point p1, Point p2)
    //{
    //  return 0.0;
    //}

    

    public RefSemanticsValueTypes()
    {
      var p1 = new Point(1, 1);
      var p2 = new Point(4, 5);

      var distance = MeasureDistance(p1, p2);
      //             ^^^^ call ambiguous
      Console.WriteLine($"Distance between {p1} and {p2} is {distance}");

      // can also pass in temporaries
      var distFromOrigin = MeasureDistance(p1, new Point());

      var alsoDistanceFromOrigin = MeasureDistance(p2, Point.Origin);

      // REF READONLY RETURNS

      // make an ordinary by-value copy
      Point copyOfOrigin = Point.Origin;

      // it's readonly, you cannot do a ref!
      //ref var messWithOrigin = ref Point.Origin;

      ref readonly var originRef = ref Point.Origin;
      // won't work
      //originRef.X = 123;
    }

    // REF STRUCTS

    // a value type that MUST be stack-allocated
    // can never be created on the heap
    // created specifically for Span<T>
    
    class CannotDoThis
    {
      Span<byte> stuff;
    }

    static void Main(string[] args)
    {
      new RefSemanticsValueTypes();

      unsafe
      {
        // managed
        byte* ptr = stackalloc byte[100];
        Span<byte> memory = new Span<byte>(ptr, 100);

        // unmanaged
        IntPtr unmanagedPtr = Marshal.AllocHGlobal(123);
        Span<byte> unmanagedMemory = new Span<byte>(unmanagedPtr.ToPointer(), 123);
        Marshal.FreeHGlobal(unmanagedPtr);
      }

      // implicit cast
      char[] stuff = "hello".ToCharArray();
      Span<char> arrayMemory = stuff;

      // string is immutable so we can make a readonly span
      ReadOnlySpan<char> more = "hi there!".AsSpan();

      Console.WriteLine($"Our span has {more.Length} elements");

      arrayMemory.Fill('x');
      Console.WriteLine(stuff);
      arrayMemory.Clear();
      Console.WriteLine(stuff);
    }
  }

C# 7.3 in Visual Studio 2017 15.5

Performance Improvement

    • Fixed-sized buffers
    • Ref local variables maybe reassigned
    • stackalloc arrays support initializers
    cs 复制代码
    int* pArr1=stackalloc int[3]{1,2,3}
    int* pArr2=stackalloc int[]{1,2,3}

Features Enhancements

  • Attributes on backing fields of auto-props (auto-props的后置字段上的属性)
cs 复制代码
[field:SomeCleverAttribute]
public float SomeProperty{get;set;}

Extensioned expression variables in initializers

cs 复制代码
public class B
{
    public B(int i,out int j)
    {
        j=i;    
    }
}
public class D:B
{
    public D(int i):B(i,out var j)
    {
        Console.WriteLine($"j = {j}}");  
    }
}
    • Tutple support == and !=
    • Imrpove overload resolution rules for method groups

New Compiler Features

    • --deterministic
    • --publicsign

What's new in C# 8

Nullable Reference Types

Method 1: 通过引用ReSharper.Annotations

[CanBeNull] Foo foo 会被解析为 Foo? foo

[CanBeNull] string middleName会被解析为string? middleName

cs 复制代码
   public class Employee
   {
       public int Id { get; set; }

       public string FirstName { get; set; }
       public string LastName { get; set; }

       [CanBeNull] public string MiddleName { get; set; }//CanBeNull is coming from ReSharper.Annotations on NuGet

       public Employee(string firstName, string lastName, [CanBeNull] string middleName) => (FirstName, LastName, MiddleName) = (firstName, lastName, middleName);

       public string FullName => $"{FirstName} {MiddleName} {LastName}";

   }

Method 2: 通过使用string?

cs 复制代码
    public class Employee
    {
        public int Id { get; set; }

        public string FirstName { get; set; }
        public string LastName { get; set; }

        public string? MiddleName { get; set; }

        //[CanBeNull] public string MiddleName { get; set; }//CanBeNull is coming from ReSharper.Annotations on NuGet

        //public Employee(string firstName, string lastName, [CanBeNull] string middleName) => (FirstName, LastName, MiddleName) = (firstName, lastName, middleName);

        public Employee(string firstName, string lastName, string? middleName) => (FirstName, LastName, MiddleName) = (firstName, lastName, middleName);

        public string FullName => $"{FirstName} {MiddleName} {LastName}";

    }

以下代码会提示一个Warning:MiddleName maybe null here. CS8602:Dereference of a possible null reference.

cs 复制代码
public string FullName => $"{FirstName} {MiddleName[0]} {LastName}";

If nullability is enabled

cs 复制代码
string? ≠ Nullable<string>

string? is still a string, but we need null checks.

cs 复制代码
public string FullName => $"{FirstName} {MiddleName?[0]} {LastName}";
cs 复制代码
        void Test()
        {
            string? str = GetString();
            //Will get warning below
            char c = str[0];

            //no warning
            if (str != null)
            {
                char c2 = str[0];
            }

        }
Override Null Checks

2 ways to stop null checks

    1. Keep the variable non-nullable
cs 复制代码
public string MiddleName { get; set; } = string.Empty;
    1. Write expression with a bang(!)
cs 复制代码
public string MiddleName { get; set; } = null!;  
cs 复制代码
            //Warning
            (null as Employee).FullName

            //No Warning
            (null as Employee)!.FullName

            //No Warning. !可以随便输入多少个,都是合法的
            (null as Employee)!!!!!!!!!.FullName
Diable nullable check

Edit **csproj file

cs 复制代码
 <Nullable>disable</Nullable>
Enable nullable check

Edit **csproj file,it is default setting.

cs 复制代码
<Nullable>enable</Nullable>

Code in below are no warning.

cs 复制代码
//No warning
Type t = Type.GetType(nameof(Employee));
string name = t.Name; 

//No warning
Type? t2 = Type.GetType(nameof(Employee));
string name = t2.Name; 

Index and Range

Points into an array

  • Value
  • IsFromEnd
cs 复制代码
    Index ids = 2;//implict conversion

    Index idx2 = new Index(0, false);

    var idx3 = ^0;//Index (0, true)
                    // -1 is not last
cs 复制代码
//Index
var items = new[] { 1, 2, 3, 4 };
items[^2] = 33;//^2 去倒数第二个数值
Console.WriteLine($"{string.Join(",", items)}");//1,2,33,4

//Range from a[X...Y] means from a[X] to a[Y].
//If X > Y will through ArgumentOutOfRangeException
items = new[] { 1, 2, 3, 4 };
var items2 = items[0..2];//从第一个开始取值,总共取两个值
Console.WriteLine($"{string.Join(",", items2)}");//1,2

//Index + Range
var items3 = items[0..^0];//从第一个开始取值,取到最后一个值
Console.WriteLine($"{string.Join(",", items3)}");//1,2,3,4

//Array slices yield copies
var test = items[..2];//Create a copy
Console.WriteLine($"{string.Join(",", test)}");//1,2
var test2 = items.AsSpan<int>();//{1,2,3,4}
Console.WriteLine("Span");
foreach (var item in test2)
{
    Console.WriteLine($"{item}");
}

Default Interface Members

Extension to create Default function in Interface

cs 复制代码
    public interface IHuman
    {
        string Name { get; set; }
    }

    public static class HumanExtension
    {
        public static void SayHello(this IHuman human)
        {
            Console.WriteLine($"Hello, I am {human.Name}");
        }
    }

Default function in iterface

It is same behavior with above code.

cs 复制代码
    public interface IHuman
    {
        string Name { get; set; }

        public void SayHello()
        {
            Console.WriteLine($"Hello, I am {Name}");
        }
    }

Call demo:

cs 复制代码
//Human human2 = new Human("Alex");
//human2.SayHello();//Compiler error

IHuman human = new Human("Alex");
human.SayHello();//Hello, I am Alex

((IHuman)new Human("Alex")).SayHello();//Hello, I am Alex

Interface override

cs 复制代码
    public class IFrieldlyHuman : IHuman//IHuman is same with code in above
    {
        public string Name { get; set; }

        public void SayHello()
        {
            Console.WriteLine($"Greeting, I am {Name}");
        }
    }

    public class Human2 : IFrieldlyHuman
    {
        public Human2(string name)
        {
            Name = name;
        }
    }

//call demo
((IFrieldlyHuman)new Human2("Alex")).SayHello();//Greeting, I am Alex

Pattern Matching

哪个人设计的这个绕的写法。。。

cs 复制代码
    struct PhoneNumer
    {
        public int Code, Number;
    }

    private void TestPhone()
    {
        var phoneNumer = new PhoneNumer();
        string? origin;
        //哪个人设计的这种写法,难读的要命
        origin = phoneNumer switch
        {
            { Number: 110 } => "Police",
            { Code: 86 } => "China",
            { } => null
        };

        //个人还是喜欢下面的写法
        switch (phoneNumer)
        {
            case { Number: 110 }:
                origin = "Police";
                break;
            case { Code: 86 }:
                origin = "China";
                break;
            default:
                origin = null;
                break;
        }
    }

What's new in C# 9 (. NET 5)

Record Types

cs 复制代码
var p = new Person() { Name = "Evan", Age = 37 };
var p2 = new Person() { Name = "Evan", Age = 37 };
Console.WriteLine(p);//Print: Person { Name = Evan, Age = 37, Address =  }
Console.WriteLine(p2);//Print: Person { Name = Evan, Age = 37, Address =  }
Console.WriteLine($"p==p2? {p == p2}");//Print: p==p2? true

var address = new Address() { AreaCode = 123456, Stress = "星火北路8号" };
var address2 = new Address() { AreaCode = 123456, Stress = "药谷大道8号" };
Console.WriteLine($"address==address2? {address == address2}");//Print: address==address2? false

p.Address = address;
p2.Address = address;
Console.WriteLine($"p==p2? {p == p2}");//Print: p==p2? true
p2.Address = address2;
Console.WriteLine($"p==p2? {p == p2}");//Print: p==p2? false

public record Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public Address Address { get; set; }
}

public class Address
{
    public int AreaCode { get; set; }
    public string Stress { get; set; }
}

浅拷贝with

cs 复制代码
Car car = new() { Engine = "V6", Color = new CarColor() { Name = "Black", Metallic = false } };
Car upgradeCar = car with { Engine = "V8" };//Clone()=shallow copy. 浅拷贝
upgradeCar.Color.Metallic = true;
Console.WriteLine(car);//Print: Car { Engine = V6, Color = CarColor { Name = Black, Metallic = True } }
Console.WriteLine(upgradeCar);//Print: Car { Engine = V8, Color = CarColor { Name = Black, Metallic = True } }
public record Car
{
    public string Engine { get; set; }
    public CarColor Color { get; set; }
}
public record CarColor
{
    public string Name { get; set; }
    public bool Metallic { get; set; }
}

Top-level Calls

Program.cs 没有namespace了。

cs 复制代码
Console.WriteLine("Hello, World!");

Foo();

void Foo()
{
    Console.WriteLine("Hello, Foo!");
}

Initial Setters

cs 复制代码
    public class Demo
    {
        //readonly filed only can set value in initial or in ctor
        public readonly string Filed = "ABC";

        public Demo(string filed)
        {
            Filed = filed;
        }
    }
cs 复制代码
    public class Demo
    {
        //Inital only can set value in the class it ctor, not by call or function
        public int Age { get; init; }

        public Demo(int filed)
        {
            Age = filed;
        }

        //Below code will get error
        public void ChangeValue(int newValue)
        {
            Age = newValue;
        }
    }

    public class CallDemo
    {
        void Main()
        {
            //Below code will get error
            var p = new Demo() { Age = 37 };
        }
    }

Pttern Matching Improvement

cs 复制代码
object obj;
if (obj is not null)
{

}
if (obj is not string)//same as !(obj is string)
{

}

int temperature = 40;
var feel = temperature switch
{
    < 0 => "冷",
    >= 0 and < 20 => "温暖",
    >= 20 and not 40 => "热",
    40 or 666 => "热死了"
};
Console.WriteLine(feel);


public static bool IsLetter(this char c) =>
        c is >= 'a' and <= 'z' or >= 'A' and <= 'Z';

public static bool IsLetterOrSeparator(this char c) =>
        c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z') or ';' or ',';

Target-Typed New

cs 复制代码
Person p=new Person();

var p2=new Person();

Person p=new();

Source Generators

Microsoft.CodeAnalysis.Analyzers, NugGet, ISourceGenerator

T4

cs 复制代码
using Microsoft.CodeAnalysis;
namespace Gen
{
    [Generator]
    public class Generator : ISourceGenerator
    {
        public void Execute(GeneratorExecutionContext context)
        {
            var source=@"class Foo{public string Bar=""bar"";}";
            context.AddSource("Gen.cs",source)
        }

        public void Execute(GeneratorInitializationContext context)
        {
         
        }
    }
}

//更改.csproj 
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>c:\temp</CompilerGeneratedFilesOutputPath>

//Build 完成后,在c:\temp\Gen\GenGenerator 会生成一个Gen.cs文件

Partial Method Syntax and Modules Initializers(部分方法语法和模块初始化器)

Startup.cs in API

What's new in C# 10 (. NET 6)

Visual Studio go to x64.

Record Structs

Same as Record class, but value types

Synthesuized members合成的成员

  • Constructure,Deconstructure,Equals/==/!=, GetHashCode, PrintMembers, ToString

Predictable differences to record classes

  • Eg: Equals() does not do null check

Performance can be significantly better than ordinary structs

  • Also better than value Tuple
  • Default Equals()/GetHaskCode() implementations are bad (boxing etc.)
  • Record struct can be 20x faster with 100% less allocations

Restrictions

  • Cannot have a clone member
  • Instance field cannot be unsafe
  • annot declare a destructor
cs 复制代码
//small, composite value types
record struct Point<T>(T x, T y){...}
Point<int> p=new (100,200);

//Record structs are mutable(可变的)
//Mutable structs are dangerous
player.Position.X++;// does nothing. means don't change anything

//Recommend:
record struct Point2<T>(T x, T y){...}
p.X++;//will not compile

Global Using Directives

using System has been replaced because we have global using System.

我们可以创建一个GlobalUsings.cs,把常用的引用放在里面。

.NET6 project included several global usings that are implicit(隐式的):

cs 复制代码
<ImplicitUsings>enable</ImplicitUsings>

File-Scoped Namespace Declarations

vs有工具去选择""Tofile-scoped namespace""

Extended Property Patterns

cs 复制代码
static void Test()
{
    object obj;
    //Check properties using dot notation
    if (obj is Developer { Manager.FirstName: "Sam" } d)
    {
        Console.WriteLine(d.Manager.FirstName);
    }

    //Check multiple patterns and arbitrary(任意的) depth
    if (obj is Developer { Manager.FirstName.Length: 5, Manager.yearOfWork: 10 } d2)
    {
        Console.WriteLine(d2.Manager.FirstName);
    }
}

public class Developer
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    public Manager Manager { get; set; }
}

public class Manager
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    public int yearOfWork { get; set; }
}

Generic Attributes通用属性

cs 复制代码
//Current approacgh to taking a type in an attribute
class MyAttribute: Attribute
{
    MyAttribute(Type type){...}//伪代码
}
//usege 
[My(typeof(Foo))]

//Now we can use type parameters instead:
class My Attribute
{
   ...
}
//usege 
[My<float>] public void Bar(){...}

//Type parameters from containing type cannot be used in attributes.eg:
public class Program<T>
{
    [SomeAttr<T>] void Foo(){...}//got error
    [SomeAttr<List<T>>] void Foo(){...}//got error
}

Lambda Improvements,提高很多

cs 复制代码
//Add attributes to lambda
var f=[A]{}=>{...};
//Demo:
var f=[return:A] x=>x;//syntax error at '=>', wrong demo
var f=[return:A] (x)=>x;//[A] lambda. Correct demo

//Multiple attributes
var f=[a1,a2][a3]()=>{};
var f2=([a1][a2,a3] int x)=>x;


//Attributes not support in delegate. will get error in below code:
f=[a] delegate {return 1;}// syntax error at 'delegate'
f=delegate ([a] int x) {return 1;}// syntax error at '['


//Collection initializers also use [a] syntax,so, the parser will differences:
var f=new C {[A]=x};//means: f[A]=x
var f2=new C {[A] x=>x};//means: f2[0]=[A] x=>x


//? (conditional element), cannot go in front:
x=b? [A];// correct
y=b? [A] ()=>{}:z;//error, syntax error at '('

//Explict return type
//You can specify an explicit return type before the parameters
f=T()=>{};//correct
f= ref int (ref int x) => ref x;//correct
f=static void (_) => {};//correct

//Not support delegate{} syntax
f= delegate int {retur 1;};// syntax error
f= delegate int (int x) {return x;};//syntax error


//Exact method type inference from lambda return:
static void F<T> (Func<T,T> f) {...}
F(int (i) => i);//Func<int,int>

//Varaibles conversion not allowed from lambda return type to delegate return type:
Func<object> f = string ()=> 'Evan';//error
Func<object?> f2 = object()=>x; //Warning


//Lambda expressions with ref return types are allowed within expressions (without additional parens圆括号)
d= ref int () =>x;// equals: d=(ref int () => x)
F(ref int() => x);// equals: F((ref int() => x))

//Var cannot be used as explicit return type
d=var (var x)=>x;//Error: contextula keyword 'var' cannot be used as explicit lambda return type

//Lambda will be infered to Action/Func<>
var f =()=>1;// System.Func<int>
var f2 = string() => "Evan";// System.Func<string>
var f3 = delegate (object o) (object o) {};// System.Action<object>

Enhanced #line directives

debug/diagnostics/if**

cs 复制代码
#if ANYCPU
...
#endif

#if DEBUG
...
#endif
相关推荐
界面开发小八哥28 分钟前
「Qt Widget中文示例指南」如何实现一个系统托盘图标?(二)
开发语言·c++·qt·用户界面
2301_7756023829 分钟前
二进制读写文件
开发语言
自身就是太阳31 分钟前
深入理解 Spring 事务管理及其配置
java·开发语言·数据库·spring
喵手35 分钟前
Java零基础-多态详解
java·开发语言·python
running thunderbolt35 分钟前
C++:类和对象全解
c语言·开发语言·c++·算法
阿雄不会写代码40 分钟前
bt量化回测框架,bt.optimize 的详细讲解,bt策略参数优化的功能,比backtrader更简单!
开发语言·python
Bob99981 小时前
电脑浏览器访问华为路由器报错,无法访问路由器web界面:ERR_SSL_VERSION_OR_CIPHER_MISMATCH 最简单的解决办法!
开发语言·javascript·网络·python·网络协议·华为·ssl
cat_fish_rain1 小时前
使用Qt 搭建简单雷达
开发语言·c++·qt
星毅要努力1 小时前
【C语言编程】【小游戏】【俄罗斯方块】
c语言·开发语言·学习·游戏
小陈的进阶之路1 小时前
c++刷题
开发语言·c++·算法