Here’s a brief summary of C# features by version.
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
1
2
|
int result;
...int.TryParse(input, out result)...
|
Now you can declare at with the out
using either the type or var
1
2
|
...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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
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:
1
2
3
4
5
6
7
|
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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.
1
2
3
4
5
6
7
8
9
10
|
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
1
2
3
|
public string FirstName { get; set; }
public string FirstName { get; private set; }
public string FirstName { get; } // set in constructor only
|
Auto property initializers
1
|
public Standing YearInSchool { get; set;} = Standing.Freshman;
|
expression bodied functions
1
2
|
public override string ToString() => $"{LastName}, {FirstName}";
public string FullName => $"{FirstName} {LastName}";
|
using static
1
|
using static System.Math;
|
And now, you can use any static method in the Math class without qualifying the Math class
null conditional operators
1
2
3
4
5
6
7
8
9
|
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
1
2
3
4
5
6
7
|
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
1
2
3
4
5
6
7
8
9
10
11
12
|
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.
1
|
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
1
2
3
4
5
6
7
8
9
10
11
12
|
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:
1
2
3
4
|
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
1
2
3
4
5
6
7
8
9
|
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
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
Comments
Comment is disabled to avoid unwanted discussions from 'localhost:1313' on your Disqus account...