With Visual Studio 2019 released, we’re counting down to .NET Core 3.0 and C# 8.0 this fall. Microsoft just announced the plan going forward: .NET 5 (coverage on VentureBeat amongst others).
.NET 5
Unless you’re a long-time enthusiast of .NET, the entire ecosystem of “.NET Framework”/”.NET Core”/”Mono”, various languages (C#/F#/etc.), and associated TLA-soup of libraries is subtle and confusing. Steering things in the same direction and unifying various sundry technologies is a goal for Fall 2020:
- Unify .NET Framework, .NET Core, and Xamarin/Mono into single platform.
- .NET Framework and .NET Core become single open-source framework/runtime: “.NET 5”.
- Mono and CoreCLR (.NET Core runtime) as alternative “runtime experiences”.
- Java interoperability on all platforms, Objective-C and Swift on some platforms.
- Improve AOT compilation story.
- Full AOT needed for iOS, WASM/WebAssembly, and some other platforms.
- Partial AOT (to reduce startup time and memory usage) combined with JIT (to maximize code support for generics, reflection, etc.) useful.
- Mono has had AOT for a while, UWP apps have .NET Native, .NET Framework has older NGEN tool.
C# 8.0 switch
We’re (most) excited about “nullable reference types” (to catch some null
-related runtime exceptions at compile-time), but there’s other good stuff coming in C# 8. The humble switch
was upgraded with pattern matching in C# 7 and gets a few new abilities in C# 8.
Here’s the starting struct from the .NET Blog post by Mads:
class Point
{
public int X { get; }
public int Y { get; }
public Point(int x, int y) => (X, Y) = (x, y);
public void Deconstruct(out int x, out int y) => (x, y) = (X, Y);
}
Switch expressions allow switch
to “return” a value:
// Before (switch as a statement)
string Display(object o)
{
switch (o)
{
case Point p:
return $"({p.X}, {p.Y})";
default:
return "unknown";
}
}
// After (switch as an expression)
string Display(object o)
{
return o switch
{
Point p => $"({p.X}, {p.Y})",
_ => "unknown"
};
}
If the end of a switch expression is reached without making a match then an expression is thrown.
Property patterns allow you to further refine matches based on accessible fields:
// Before (switch as a statement)
string Display(object o)
{
switch (o)
{
case Point p when p.X == 0 && p.Y == 0:
return "origin";
//...
}
}
// After (switch as an expression)
string Display(object o)
{
return o switch
{
Point { X: 0, Y: 0 } /* p */ => "origin",
Point { X: var x, Y: var y } /* p */ => $"({x}, {y})",
{} => o.ToString(), // `o` is non-null and not a `Point`
null => "null"
};
}
Positional patterns work with tuples and types- like Point
- that have a Deconstruct()
method (see deconstructors):
string Display(object o)
{
return o switch
{
Point(0, 0) => "origin",
Point(var x, var y) => $"({x}, {y})",
//...
}
}
// General case for tuples
string DisplayXY(int x, int y, bool isValid)
(x, y, isValid) switch {
(_, _, false) => "invalid",
(0, 0, true) => "origin",
//...
}
Overall, I’m not thrilled about XYZ switch
syntax instead of switch XYZ
, but these are otherwise welcome additions (especially coming from other languages with a similar feature).
For a longer write up also see this recent MSDN magazine article.