Lomont.org

(Also lomonster.com and clomont.com)

New C# syntax and features

Published

Here’s a brief summary of C# features by vesion.

New C# syntax and features

New in 7.0

out variables

Before you had to declare a variable before using it as an out parameter

int result;
...int.TryParse(input, out result)...

Now you can declare at with the out using either the type or var

...int.TryParse(input, out int result)...
...int.TryParse(input, out var result)...

Tuples

Tuples are nicer to use as return items and as quick classes. (At the moment you need to add System.ValueTuple from Microsoft at nuget.org to use them. They will be integrated soon). Names are optional. Deconstruction requires a deconstruction function in a class/struct. Deconstructors use out parameters instead of return tuples to allow overloading on types/number of parameters.

var letters = ("a", "b");
(string Alpha, string Beta) letters = ("a", "b");
var alphabetStart = (Alpha: "a", Beta: "b");

private static (int Max, int Min) Range(IEnumerable<int> numbers)
{
    int min = int.MaxValue;
    int max = int.MinValue;
    foreach(var n in numbers)
    {
        min = (n < min) ? n : min;
        max = (n > max) ? n : max;
    }
    return (max, min);
}


var range = Range(numbers);
(int max, int min) = Range(numbers);

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

    public double X { get; }
    public double Y { get; }

    public void Deconstruct(out double x, out double y)
    {
        x = this.X;
        y = this.Y;
    }
}
var p = new Point(3.14, 2.71);
(double X, double Y) = p;

(string DirectoryName, string FileName, string Extension) pathData =
    (DirectoryName: @"\\test\unc\path\to",
    FileName: "something",
    Extension: ".ext");

Pattern matching

Can match and assign variables in one pass:

  if ((storage is UsbKey usbDrive) && usbDrive.IsPluggedIn)
  {
    usbDrive.Unload();
    Console.WriteLine("USB Drive Unloaded.");
  }
  else if (storage is DVD dvd && dvd.IsInserted)
  // ...

Pattern matching supports is expressions and switch expressions

  switch(storage)
  {
    case UsbKey usbKey when usbKey.IsPluggedIn:
      usbKey.Unload();
      Console.WriteLine("USB Drive Unloaded.");
      break;
    case DVD dvd when dvd.IsInserted:
      dvd.Eject();
      break;
    case HardDrive hardDrive:
      throw new InvalidOperationException();
    case null:
    default:
      throw new ArgumentNullException();
  }

TODO

ref locals and returns

Can return a ref into an item now:

public ref byte GetNth(byte[] arr, int n, byte val)
{
  for (var i = 0; i < arr.Length; ++i)
     {
     if (b == val) n--;
     if (n == 0) 
        return ref arr[i];
     }
  return null;     
}

ref byte b = GetNth(arr,10,3);
b = 11; // update value in place

TODO

local functions

C# 7.0 supports functions local to another function.

public Task<string> PerformLongRunningWork(string address, int index, string name)
{
    if (string.IsNullOrWhiteSpace(address))
        throw new ArgumentException(message: "An address is required", paramName: nameof(address));
    if (index < 0)
        throw new ArgumentOutOfRangeException(paramName: nameof(index), message: "The index must be non-negative");
    if (string.IsNullOrWhiteSpace(name))
        throw new ArgumentException(message: "You must supply a name", paramName: nameof(name));

    return longRunningWorkImplementation();

    async Task<string> longRunningWorkImplementation()
    {
        var interimResult = await FirstWork(address);
        var secondResult = await SecondStep(index, name);
        return $"The results are {interimResult} and {secondResult}. Enjoy.";
    }
}

More expression bodied members

In C# 7, you can implement constructors, finalizers, and get and set accessors on properties and indexers. The following code shows examples of each:

TODO

class TemporaryFile  // Full IDisposible implementation
                     // left off for elucidation.
{
  public TemporaryFile(string fileName) =>
    File = new FileInfo(fileName);
  ~TemporaryFile() => Dispose();
  Fileinfo _File;
  public FileInfo File
  {
    get => _File;
    private set => _File = value;
  }
  void Dispose() => File?.Delete();
}

throw expressions

TODO

public string Name
{
    get => name;
    set => name = value ?? 
        throw new ArgumentNullException(paramName: nameof(value), message: "New name must not be null");
}

generalized async types
todo - complicated
public async ValueTask<int> Func()
{
    await Task.Delay(100);
    return 5;
}

numeric literal syntax

binary literals, and digit separators.

public const int One =  0b0001;
public const int Two =  0b0010;
public const int Four = 0b0100;
public const int Eight = 0b1000;
public const int Sixteen =   0b0001_0000;
public const int ThirtyTwo = 0b0010_0000;
public const int SixtyFour = 0b0100_0000;
public const int OneHundredTwentyEight = 0b1000_0000;
public const 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;

New in 6.0

Read only auto properties

public string FirstName { get; set; }
public string FirstName { get; private set; }
public string FirstName { get; } // set in constructor only

Auto property initializers

public Standing YearInSchool { get; set;} = Standing.Freshman;

expression bodied functions

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

using static

using static System.Math;

And now, you can use any static method in the Math class without qualifying the Math class

null conditional operators

var first = person?.FirstName; 
first = person?.FirstName ?? "Unspecified";

var handler = this.SomethingHappened;
if (handler != null)
    handler(this, eventArgs);

// preferred in C# 6:
this.SomethingHappened?.Invoke(this, eventArgs);

string interpolation

public string FullName => $"{FirstName} {LastName}";
public string GetFormattedGradePoint() =>
    $"Name: {LastName}, {FirstName}. G.P.A: {Grades.Average()}";
public string GetGradePointPercentage() =>
    $"Name: {LastName}, {FirstName}. G.P.A: {Grades.Average():F2}";
public string GetGradePointPercentages() =>
    $"Name: {LastName}, {FirstName}. G.P.A: {Grades.Any() ? Grades.Average() : double.NaN:F2}";

exception filters

public static async Task<string> MakeRequest()
{ 
    var client = new System.Net.Http.HttpClient();
    var streamTask = client.GetStringAsync("https://localHost:10000");
    try {
        var responseText = await streamTask;
        return responseText;
    } catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("301"))
    {
        return "Site Moved";
    }
}

nameof expression

The nameof expression evaluates to the name of a symbol, useful in XAML.

PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(LastName)));

await in catch and finally blocks

C# 5 had several limitations around where you could place await expressions. One of those has been removed in C# 6. You can now use await in catch or finally expressions.

index initializers

private List<string> messages = new List<string> 
{
    "Page not Found",
    "Page moved, but left a forwarding address.",
    "The web server can't come out to play today."
};
private Dictionary<int, string> webErrors = new Dictionary<int, string>
{
    [404] = "Page not Found",
    [302] = "Page moved, but left a forwarding address.",
    [500] = "The web server can't come out to play today."
};

extension methods for collection initializers

Makes adding initializer lists to a custom collection Now you can, but only if you create an extension method that maps Add to Enroll:

public static class StudentExtensions
{
    public static void Add(this Enrollment e, Student s) => e.Enroll(s);
}

improved overload resolution

TODO?

New in 5.0

Async

Too big to cover here :) TODO - base format and notes? https://msdn.microsoft.com/en-us/library/hh873175(v=vs.110).aspx

Caller information

private static void Log(string message, 
  [CallerMemberName] string methodName = null, 
  [CallerFilePath] string sourceFile = null,
  [CallerLineNumber] int lineNumber = 0)
{
  Console.WriteLine("[{2} -- {0}] - {1} ({3}:{4})", 
    methodName, message, DateTime.Now,   
    sourceFile, lineNumber);
}

New in 4.0

Late binding (dynamic)

Named arguments

Optional parameters

COM support

New in 3.0

Lambda expressions

Extension methods

Expression tree

Anonymous types

LINQ

implicit typing (var)

New in 2.0

Generics

Anonymous methods

Nullable types

The End :)

END OF LIST

Categories: Software

Tags: Software C#

Comments

Comment is disabled to avoid unwanted discussions from 'localhost:1313' on your Disqus account...