Code demonstration C ා new functions of each version

Code demonstration C ා new functions of each version

In fact, the new functions of each version can be found on the official website, but few people put them together and demonstrate each new feature through very short code.

1. C × 2.0 - 2005

1.1 generic

Generics in Java do not support value types and run-time type erasure. NET is better.

// Declare the generic class.
public class GenericList<T>
{
    public void Add(T input) { }
}
class TestGenericList
{
    private class ExampleClass { }
    static void Main()
    {
        // Declare a list of type int.
        GenericList<int> list1 = new GenericList<int>();
        list1.Add(1);

        // Declare a list of type string.
        GenericList<string> list2 = new GenericList<string>();
        list2.Add("");

        // Declare a list of type ExampleClass.
        GenericList<ExampleClass> list3 = new GenericList<ExampleClass>();
        list3.Add(new ExampleClass());
    }
}
1.2 division type

It is possible to split the definition of a class, a structure, an interface, or a method into two or more files. Each source file contains a part of the type or method definition that will be combined when the application is compiled.

public partial class Employee
{
    public void DoWork()
    {
    }
}
public partial class Employee
{
    public void GoToLunch()
    {
    }
}
1.3 anonymous method
Func<int, int, int> sum = delegate (int a, int b) { return a + b; };
Console.WriteLine(sum(3, 4));  // output: 7
1.4 null able value types
double? pi = 3.14;
char? letter = 'a';

int m2 = 10;
int? m = m2;

bool? flag = null;

// An array of a nullable type:
int?[] arr = new int?[10];
1.5 iterator
static void Main()
{
    foreach (int number in SomeNumbers())
    {
        Console.Write(number.ToString() + " ");
    }
    // Output: 3 5 8
    Console.ReadKey();
}

public static System.Collections.IEnumerable SomeNumbers()
{
    yield return 3;
    yield return 5;
    yield return 8;
}
1.6 covariance and inversion

In C, covariance and inversion can realize implicit reference conversion of array type, delegate type and generic type parameters. Covariant preserves allocation compatibility, while contravariant preserves allocation compatibility.

// Assignment compatibility.   
string str = "test";  
// An object of a more derived type is assigned to an object of a less derived type.   
object obj = str;  
  
// Covariance.   
IEnumerable<string> strings = new List<string>();  
// An object that is instantiated with a more derived type argument   
// is assigned to an object instantiated with a less derived type argument.   
// Assignment compatibility is preserved.   
IEnumerable<object> objects = strings;  
  
// Contravariance.             
// Assume that the following method is in the class:   
// static void SetObject(object o) { }   
Action<object> actObject = SetObject;  
// An object that is instantiated with a less derived type argument   
// is assigned to an object instantiated with a more derived type argument.   
// Assignment compatibility is reversed.   
Action<string> actString = actObject;

2. C × 3.0-2007

2.1 properties of automatic implementation
// This class is mutable. Its data can be modified from
// outside the class.
class Customer
{
    // Auto-implemented properties for trivial get and set
    public double TotalPurchases { get; set; }
    public string Name { get; set; }
    public int CustomerID { get; set; }

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

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

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

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

        // Modify a property.
        cust1.TotalPurchases += 499.99;
    }
}
2.2 anonymous type
var v = new { Amount = 108, Message = "Hello" };  
  
// Rest the mouse pointer over v.Amount and v.Message in the following  
// statement to verify that their inferred types are int and n .  
Console.WriteLine(v.Amount + v.Message);
2.3 query expression (LINQ)

LINQ allows you to write C code like SQL, like this:

from p in persons
where p.Age > 18 && p.IsBeatiful
select new
{
    p.WeChatId, 
    p.PhoneNumber
}

The significance of LINQ is that C ා has made significant adjustments. lambda expressions, extension methods, expression trees, anonymous types, automatic attributes, etc. mentioned in this chapter are all necessary components of LINQ.
Because the form of extension method can also get consistent results and make the code style more consistent, I usually use less LINQ syntax:

// Same as the above code, but changed to extended method style:
persons
    .Where(x => x.Age > 18 && x.IsBeatiful)
    .Select(x => new 
    {
        x.WeChatId, 
        x.PhoneNumber, 
    });
2.4 Lambda expression
Func<int, int> square = x => x * x;
Console.WriteLine(square(5));
// Output:
// 25
2.5 expression tree

This is one of the foundations of LINQ. Its purpose is to store code in memory like data, and then reinterpret / execute these "code data" later.
Entity Framework is a classic scenario. It first saves the expression tree, and then translates it into SQL and sends it to the database for execution.

Note: the expression tree does not represent all the codes. The syntax after C × 3.0, including???,???, async await, optional parameters, etc., cannot be placed in the expression tree. Officials are said to be ready to update it, but there has been no progress.

Insert a code slice here
2.6 extension method

Extension methods enable you to "add" methods to an existing type without creating a new derived type, recompiling, or otherwise modifying the original type.

static void Main()
{
    Console.WriteLine ("Perth".IsCapitalized());    
    // Equivalent to:
    Console.WriteLine (StringHelper.IsCapitalized ("Perth"));   
    
    // Interfaces can be extended, too:
    Console.WriteLine ("Seattle".First());   // S
}

public static class StringHelper
{
    public static bool IsCapitalized (this string s)
    {
        if (string.IsNullOrEmpty(s)) return false;
        return char.IsUpper (s[0]);
    }
    
    public static T First<T> (this IEnumerable<T> sequence)
    {
        foreach (T element in sequence)
            return element;
        
        throw new InvalidOperationException ("No elements!");
    }
}
2.7 var
var i = 10; // Implicitly typed.
int i = 10; // Explicitly typed.
2.8 segment method
namespace PM
{
    partial class A
    {
        partial void OnSomethingHappened(string s);
    }

    // This part can be in a separate file.
    partial class A
    {
        // Comment out this method and the program
        // will still compile.
        partial void OnSomethingHappened(String s)
        {
            Console.WriteLine("Something happened: {0}", s);
        }
    }
}
2.9 object and set initializers
public class Cat
{
    // Auto-implemented properties.
    public int Age { get; set; }
    public string Name { get; set; }

    public Cat()
    {
    }

    public Cat(string name)
    {
        this.Name = name;
    }
}

3. C × 4.0-2010

3.1 dynamic

This is a feature that makes the CLR have to make a change. With this, C ා can also write code like js, php, python and other weakly typed languages.

dynamic a = 3;
a = 3.14;
a = "Hello World";
a = new[] { 1, 2, 3, 4, 5 };
a = new Func<int>(() => 3);
a = new StringBuilder();
Console.WriteLine(a.GetType().Name); // StringBuilder

Note that dynamic can represent anything, including arrays, delegates, and so on. Abusing dynamic makes programs difficult to maintain.

3.2 named parameters / optional parameters
PrintOrderDetails(productName: "Red Mug", sellerName: "Gift Shop", orderNum: 31);
public void ExampleMethod(int required, string optionalstr = "default string",
    int optionalint = 10)
3.3 covariance and inversion in generics
IEnumerable<Derived> d = new List<Derived>();
IEnumerable<Base> b = d;

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

3.4 type equivalent, built-in interoperability type

This is mainly for interaction with COM. Previously, you need to reference some COM type related assemblies. Now you can directly reference COM.
For details, please refer to: https://docs.microsoft.com/zh-cn/dotnet/framework/interop/type-equivalence-and-embedded-interop-types

4. C × 5.0-2012

4.1 async/await

The essence of async/await is a state machine, like IEnumerable. Previously, the game engine Unity only supported C × 3.0, so it used IEnumerable to send Http requests with state machine. async/await has two advantages, one is to avoid UI thread stuck, the other is to improve system throughput and ultimately improve performance.

private DamageResult CalculateDamageDone()
{
    // Code omitted:
    //
    // Does an expensive calculation and returns
    // the result of that calculation.
}

calculateButton.Clicked += async (o, e) =>
{
    // This line will yield control to the UI while CalculateDamageDone()
    // performs its work.  The UI thread is free to perform other work.
    var damageResult = await Task.Run(() => CalculateDamageDone());
    DisplayDamage(damageResult);
};
4.2 caller information

Note that this is generated at compile time, so it's more guaranteed than StackTrace.

public void DoProcessing()
{
    TraceMessage("Something happened.");
}

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

// Sample Output:
//  message: Something happened.
//  member name: DoProcessing
//  source file path: c:\Visual Studio Projects\CallerInfoCS\CallerInfoCS\Form1.cs
//  source line number: 31

5. C × 6.0-2015

5.1 static import

Finally, you don't need to write static class names.

using static System.Math;
using static System.Console;

WriteLine(Sin(3.14)); // 0.00159265291648683
5.2 exception filter

In try catch, you can catch according to the specified conditions, other conditions are not catch.

public static async Task<string> MakeRequest()
{
    WebRequestHandler webRequestHandler = new WebRequestHandler();
    webRequestHandler.AllowAutoRedirect = false;
    using (HttpClient client = new HttpClient(webRequestHandler))
    {
        var stringTask = client.GetStringAsync("https://docs.microsoft.com/en-us/dotnet/about/");
        try
        {
            var responseText = await stringTask;
            return responseText;
        }
        catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("301"))
        {
            return "Site Moved";
        }
    }
}
5.3 automatic initialization expression
public ICollection<double> Grades { get; } = new List<double>();
5.4 expression bodied function members
public override string ToString() => $"{LastName}, {FirstName}";
5.5 Null propagator
var first = person?.FirstName;
5.6 string interpolation
public string GetGradePointPercentage() =>
    $"Name: {LastName}, {FirstName}. G.P.A: {Grades.Average():F2}";
5.7 name of expression
if (IsNullOrWhiteSpace(lastName))
    throw new ArgumentException(message: "Cannot be blank", paramName: nameof(lastName));
5.8 index initializer

Another feature that makes collection initialization easier is the use of extension methods for the Add method. This feature is added for Visual Basic parity. This is useful if the method of a custom collection class has the name to Add a new item semantically.

6. C × 7.0 version - 2017

6.1 out variable
if (int.TryParse(input, out int result))
    Console.WriteLine(result);
else
    Console.WriteLine("Could not parse input");
6.2 tuples and destructors

As shown in the above code, deconstruction can split tuples into multiple variables.

(string Alpha, string Beta) namedLetters = ("a", "b");
Console.WriteLine($"{namedLetters.Alpha}, {namedLetters.Beta}");
6.3 pattern matching

Now you can automatically convert to a variable of this type when matching a type. If the conversion fails, the variable will be assigned the default value (null or 0).

  • Minimalist Edition
if (input is int count)
    sum += count;
  • switch/case version:
public static int SumPositiveNumbers(IEnumerable<object> sequence)
{
    int sum = 0;
    foreach (var i in sequence)
    {
        switch (i)
        {
            case 0:
                break;
            case IEnumerable<int> childSequence:
            {
                foreach(var item in childSequence)
                    sum += (item > 0) ? item : 0;
                break;
            }
            case int n when n > 0:
                sum += n;
                break;
            case null:
                throw new NullReferenceException("Null found in sequence");
            default:
                throw new InvalidOperationException("Unrecognized type");
        }
    }
    return sum;
}
6.4 local functions

This is mainly convenient. javascript can write like this.
The advantage of lambda is that it can be defined at the back, and lambda must be defined at the front.

public static IEnumerable<char> AlphabetSubset3(char start, char end)
{
    if (start < 'a' || start > 'z')
        throw new ArgumentOutOfRangeException(paramName: nameof(start), message: "start must be a letter");
    if (end < 'a' || end > 'z')
        throw new ArgumentOutOfRangeException(paramName: nameof(end), message: "end must be a letter");

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

    return alphabetSubsetImplementation();

    IEnumerable<char> alphabetSubsetImplementation()
    {
        for (var c = start; c < end; c++)
            yield return c;
    }
}
6.5 more expression bodied members

This function can make some functions written in the form of expressions, which is very convenient.

// 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";
}
6.6 Ref local variables and return results

This feature allows you to use and return algorithms that reference variables that are defined elsewhere. One example is to use a large matrix and find a single location with certain characteristics.

The main purpose of this function is to improve the performance of the value type and let it really play its role. C + + has similar functions.

public static ref int Find(int[,] matrix, Func<int, bool> predicate)
{
    for (int i = 0; i < matrix.GetLength(0); i++)
        for (int j = 0; j < matrix.GetLength(1); j++)
            if (predicate(matrix[i, j]))
                return ref matrix[i, j];
    throw new InvalidOperationException("Not found");
}

ref var item = ref MatrixSearch.Find(matrix, (val) => val == 42);
Console.WriteLine(item);
item = 24;
Console.WriteLine(matrix[4, 2]);
6.7 abandon yuan

In general, when you deconstruct a tuple or call a method with an out parameter, you have to define a variable whose value doesn't matter and you don't intend to use it. In order to deal with this situation, C Chen added support for abandoning yuan. A discard element is a write only variable named ou that can be given all the values to discard to a single variable. Discards are similar to unassigned variables; they cannot be used in code (except for assignment statements).

using System;
using System.Collections.Generic;

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

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

        return ("", 0, 0, 0, 0, 0);
    }
}
// The example displays the following output:
//      Population change, 1960 to 2010: 393,149
6.8 binary text and number separator

This is used to make numbers and binaries more readable.

// Binary text:
public const int Sixteen =   0b0001_0000;
public const int ThirtyTwo = 0b0010_0000;
public const int SixtyFour = 0b0100_0000;
public const int OneHundredTwentyEight = 0b1000_0000;

// Number separator:
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;
6.9 throw expression

Throw must be a statement before, so sometimes you have to write more code to complete the required functions. But 7.0 provides a throw expression to make the code simpler and easier to read.

void Main()
{
    // You can now throw expressions in expressions clauses.
    // This is useful in conditional expressions:
    
    string result = new Random().Next(2) == 0 ? "Good" : throw new Exception ("Bad");
    result.Dump();
    
    Foo().Dump();
}

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

7. C × 8.0 - 2019

7.1 Readonly members

Readonly members

public readonly override string ToString() =>
    $"({X}, {Y}) is {Distance} from the origin";
7.2 default interface method

Methods can also be defined in the interface, and this new function is often debated. But think about it, sometimes it is to define the interface first, and the implementation of the interface needs to realize many related but tedious functions, such as ILogger in ASP.NET Core, who uses who knows, especially many methods need to be implemented, but they are almost the same. So this function is really necessary.

void Main()
{
    ILogger foo = new Logger();
    foo.Log (new Exception ("test"));   
}

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

interface ILogger
{
    void Log (string message);  
    
    // Adding a new member to an interface need not break implementors:
    public void Log (Exception ex) => Log (ExceptionHeader + ex.Message);
    
    // The static modifier (and other modifiers) are now allowed:
    static string ExceptionHeader = "Exception: ";
}
7.3 pattern matching enhancement

This is for simplifying code and functional programming. I like it very much.

  • Attribute mode
public static decimal ComputeSalesTax(Address location, decimal salePrice) =>
    location switch
    {
        { State: "WA" } => salePrice * 0.06M,
        { State: "MN" } => salePrice * 0.75M,
        { State: "MI" } => salePrice * 0.05M,
        // other cases removed for brevity...
        _ => 0M
    };
  • Tuple mode
public static string RockPaperScissors(string first, string second)
    => (first, second) switch
    {
        ("rock", "paper") => "rock is covered by paper. Paper wins.",
        ("rock", "scissors") => "rock breaks scissors. Rock wins.",
        ("paper", "rock") => "paper covers rock. Paper wins.",
        ("paper", "scissors") => "paper is cut by scissors. Scissors wins.",
        ("scissors", "rock") => "scissors is broken by rock. Rock wins.",
        ("scissors", "paper") => "scissors cuts paper. Scissors wins.",
        (_, _) => "tie"
    };
  • Location mode
static Quadrant GetQuadrant(Point point) => point switch
{
    (0, 0) => Quadrant.Origin,
    var (x, y) when x > 0 && y > 0 => Quadrant.One,
    var (x, y) when x < 0 && y > 0 => Quadrant.Two,
    var (x, y) when x < 0 && y < 0 => Quadrant.Three,
    var (x, y) when x > 0 && y < 0 => Quadrant.Four,
    var (_, _) => Quadrant.OnBorder,
    _ => Quadrant.Unknown
};
  • switch expression
    This function can change the code from a large number of if/else or switch/case to "one line code", which conforms to the idea of functional programming and is very easy to use
public static RGBColor FromRainbow(Rainbow colorBand) =>
    colorBand switch
    {
        Rainbow.Red    => new RGBColor(0xFF, 0x00, 0x00),
        Rainbow.Orange => new RGBColor(0xFF, 0x7F, 0x00),
        Rainbow.Yellow => new RGBColor(0xFF, 0xFF, 0x00),
        Rainbow.Green  => new RGBColor(0x00, 0xFF, 0x00),
        Rainbow.Blue   => new RGBColor(0x00, 0x00, 0xFF),
        Rainbow.Indigo => new RGBColor(0x4B, 0x00, 0x82),
        Rainbow.Violet => new RGBColor(0x94, 0x00, 0xD3),
        _              => throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand)),
    };
7.4 using statement
static int WriteLinesToFile(IEnumerable<string> lines)
{
    using var file = new System.IO.StreamWriter("WriteLines2.txt");
    // Notice how we declare skippedLines after the using statement.
    int skippedLines = 0;
    foreach (string line in lines)
    {
        if (!line.Contains("Second"))
        {
            file.WriteLine(line);
        }
        else
        {
            skippedLines++;
        }
    }
    // Notice how skippedLines is in scope here.
    return skippedLines;
    // file is disposed here
}
7.5 static local functions

Compared with non static local functions, static local functions do not have closures, so less code is generated and the performance is easier to control.

int M()
{
    int y = 5;
    int x = 7;
    return Add(x, y);

    static int Add(int left, int right) => left + right;
}
7.6 asynchronous flow

This function corresponds to IEnumerable and Task. A classic table is as follows:

Single value Multivalued
synchronization T
asynchronous Task

Among them, the question mark? Finally, it is called asynchronous flow - IAsyncEnumerable:

public static async System.Collections.Generic.IAsyncEnumerable<int> GenerateSequence()
{
    for (int i = 0; i < 20; i++)
    {
        await Task.Delay(100);
        yield return i;
    }
}

Unlike IEnumerable, IAsyncEnumerable system has no built-in extension method, so it may not be as convenient as IEnumerable, but it can achieve the same (or better) effect as IEnumerable by installing NuGet package f.

7.7 index and scope

Indexes and ranges, like slicers in Python, are just - replaced by ^.

var words = new string[]
{
    // index from start    index from end
    "The",      // 0                   ^9
    "quick",    // 1                   ^8
    "brown",    // 2                   ^7
    "fox",      // 3                   ^6
    "jumped",   // 4                   ^5
    "over",     // 5                   ^4
    "the",      // 6                   ^3
    "lazy",     // 7                   ^2
    "dog"       // 8                   ^1
};              // 9 (or words.Length) ^0

var quickBrownFox = words[1..4];
var lazyDog = words[^2..^0];
var allWords = words[..]; // contains "The" through "dog".
var firstPhrase = words[..4]; // contains "The" through "fox"
var lastPhrase = words[6..]; // contains "the", "lazy" and "dog"
7.8 Null merge assignment
List<int> numbers = null;
int? i = null;

numbers ??= new List<int>();
numbers.Add(i ??= 17);
numbers.Add(i ??= 20);

Console.WriteLine(string.Join(" ", numbers));  // output: 17 17
Console.WriteLine(i);  // output: 17
7.9 unmanaged construction type

As with any unmanaged type, you can create a pointer to a variable of this type, or allocate memory blocks on the stack for instances of this type

Span<Coords<int>> coordinates = stackalloc[]
{
    new Coords<int> { X = 0, Y = 0 },
    new Coords<int> { X = 0, Y = 3 },
    new Coords<int> { X = 4, Y = 0 }
};
7.10 staccalloc in nested expressions
Span<int> numbers = stackalloc[] { 1, 2, 3, 4, 5, 6 };
var ind = numbers.IndexOfAny(stackalloc[] { 2, 4, 6 ,8 });
Console.WriteLine(ind);  // output: 1

8. Original link

https://akiyax.github.io/new-features-in-csharp/.

9. References

C ා development history - C ා Guide
Microsoft Docs https://docs.microsoft.com/zh-cn/dotnet/csharp/whats-new/csharp-version-history

45 original articles published, praised 8, visited 30000+
Private letter follow

Tags: less Lambda SQL Python

Posted on Sun, 15 Mar 2020 00:36:16 -0400 by belick