当前位置: 首页 > article >正文

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

目录

What's new in C# 7

C# 7 in Visual Studio 2017

Out Variables

Pattern Matching

Tuples (System.ValueTuple)

 Deconstruct解构

Local Functions

Ref Returns and Locals

Expression Bodied Members

Throw Expressions

Generated Async Return Types

Literal Improvements

C# 7.1 in Visual Studio 2017.3

Compilation Issues (how to switch to C#7.1)

Async Main

Default Expressions

Ref Assemblies

Infer Tuple Names

Pattern-Matching with Generics

C# 7.2 in Visual Studio 2017 15.5

Leading Digit Separators

'Private Protected' Access Modifier

Non-Trailing Named Arguments

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

C# 7.3 in Visual Studio 2017 15.5

Performance Improvement

Features Enhancements

Extensioned expression variables in initializers

New Compiler Features

What's new in C# 8

Nullable Reference Types

Method 1: 通过引用ReSharper.Annotations

Method 2: 通过使用string?

Override Null Checks

Diable nullable check

Enable nullable check

Index and Range

Default Interface Members

Extension to create Default function in Interface

Default function in iterface

Pattern Matching

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

Record Types

浅拷贝with

Top-level Calls

Initial Setters

Pttern Matching Improvement

Target-Typed New

Source Generators

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

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

Record Structs

Global Using Directives

File-Scoped Namespace Declarations

Extended Property Patterns

Generic Attributes通用属性

Lambda Improvements,提高很多

Enhanced #line directives



What's new in C# 7

C# 7 in Visual Studio 2017

Out Variables

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

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)

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解构

// 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体内,或者方法体外,放在方法体内是,所在的位置可以在调用前或者调用后面。

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

  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);

  }
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

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

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

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

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

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去解决编译错误。

static async Task Main(string[] args)
    {

      Console.WriteLine("ABC");
    }

Async Main

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

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

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

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

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

      // hex
      var y = 0x_baad_f00d;
    }
  }

'Private Protected' Access Modifier

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

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

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
    • 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的后置字段上的属性)
[field:SomeCleverAttribute]
public float SomeProperty{get;set;}

Extensioned expression variables in initializers

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

   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?

    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.

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

If nullability is enabled 

string? ≠ Nullable<string>

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

public string FullName => $"{FirstName} {MiddleName?[0]} {LastName}";
        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
public string MiddleName { get; set; } = string.Empty;
  • 2. Write expression with a bang(!)
public string MiddleName { get; set; } = null!;  


            //Warning
            (null as Employee).FullName

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

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

Edit **csproj file

 <Nullable>disable</Nullable>
Enable nullable check

Edit **csproj file,it is default setting.

<Nullable>enable</Nullable>

Code in below are no warning. 

//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
    Index ids = 2;//implict conversion

    Index idx2 = new Index(0, false);

    var idx3 = ^0;//Index (0, true)
                    // -1 is not last
//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

    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.

    public interface IHuman
    {
        string Name { get; set; }

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

Call demo:

//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

    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

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

    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


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

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了。


Console.WriteLine("Hello, World!");

Foo();

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

Initial Setters

    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;
        }
    }
    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

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

Person p=new Person();

var p2=new Person();

Person p=new();

Source Generators

Microsoft.CodeAnalysis.Analyzers, NugGet, ISourceGenerator

T4

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
//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(隐式的):

<ImplicitUsings>enable</ImplicitUsings>

File-Scoped Namespace Declarations

vs有工具去选择“”Tofile-scoped namespace“”

Extended Property Patterns

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通用属性

//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,提高很多

//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**

#if ANYCPU
...
#endif

#if DEBUG
...
#endif


http://www.kler.cn/a/308473.html

相关文章:

  • Nuxt 版本 2 和 版本 3 的区别
  • 前端开发中常用的包管理器(npm、yarn、pnpm、bower、parcel)
  • 多叉树笔记
  • 10款PDF翻译工具的探索之旅:我的使用经历与工具特色!!
  • jwt用户登录,网关给微服务传递用户信息,以及微服务间feign调用传递用户信息
  • 第二节 OSI-物理层
  • 牛客周赛 Round 60(A,B,C,D,E,F)
  • 构建“零工市场小程序”,服务灵活就业“大民生”
  • Baumer工业相机堡盟工业相机如何通过BGAPI SDK设置相机的图像剪切(ROI)功能(C语言)
  • com.microsoft.sqlserver:sqljdbc4:jar:4.0 was not found产生原因及解决步骤
  • 电商店群模式如何利用云分账实现自动化资金管理
  • 闲云野记:24915
  • 技术上,如何复现 o1?
  • 易于理解和实现的Python代码示例
  • 数据中心服务器与存储运维的深度实践与挑战
  • 部署自己的对话大模型,使用Ollama + Qwen2 +FastGPT 实现
  • ThinkCMF框架任意内容包含漏洞的讲解
  • 简化登录流程,助力应用建立用户体系
  • 《程序猿之设计模式实战 · 池化思想》
  • MySql批量迁移数据库
  • macOS使用brew安装并配置python环境
  • visual studio2015安装番茄助手
  • Spring Boot-日志相关问题
  • android13隐藏桌面底部白线
  • STM32巡回研讨会总结(2024)
  • Kafka日志索引详解与常见问题分析