As one of my languages du jour, I've always had a fondness for C#. It was one of the first high-level languages that I learned in college and it's been part of my daily professional life for the better part of ten years. One of the things that's always been enjoyable about the language is the concept of constant innovation.
Features are constantly being adapted from other languages (looking at you F#, Kotlin, Typescript, and more) to help add features that would be greatly desired, extend existing functionality, or just apply an ample sprinkling of syntax sugar. So I thought with the new year just beginning, let's take a look at some of the proposed features that are slated to find their way into the flagship Microsoft language in the near future!
It's worth noting that this is by no means a complete list and everything in this list, as with all things in active development, is subject to change / never happen.
So, let's get into it!
Your Results May Covary
Have you ever been writing a class and found yourself just inventing new methods so that you can return some type from a base class since C# has always restricted you to only return the explicit class itself?
This proposal outlines what would be support for covariant return types, which would allow you to override an existing method using a more specific implementation than the original method.
- class Animal
- {
- virtual Animal GenerateFromEnvironment(EnvironmentConfiguration config);
- }
Now previously, if you wanted to extend your Animal class to support something like a Dinosaur, you'd have to either write some hacky bridge method or throw some logic in there to specifically check for the existence of a Dinosaur. Covariant returns will allow you to still override the same base class / methods, but return the more specific implementations you are looking for your class:
- class Dinosaur : Animal
- {
-
- override Dinosaur GenerateFromEnvironment(EnvironmentConfiguration config);
- }
Support for this feature does not require the targeted class to implement the IDisposible interface, but instead just contain a Dispose() method.
A "New" New
Let's say you had some complex collection like the following that you wanted to initialize upon declaration:
- Dictionary<string, List<string>> words = new Dictionary<string, List<string>>() {
- { "foo", new List<string>(){ "foo", "bar", "buzz" } }
- };
This proposal simplifies the use of
new expressions since it already knows the types, so instead of using
new Dictionary<string, List<string>>(), you can just use
new():
- Dictionary<string, List<int>> words = new() {
- { "foo", new() { "foo", "bar", "buzz" } }
- };
Null Checks Kotlinified No More
No one really likes writing countless null checks. Lines upon lines of if statements with nested exceptions being thrown, it's gross. In a vein similar to the previous feature,
this proposal aims to eliminate the need for explicit null checking by allowing an operator ! to be added to a parameter to indicate to the compiler that a given value
will not be null.
It would transform a snippet like the following,
-
- int CountWords(string sentence)
- {
- if (sentence is null)
- {
- throw new ArgumentNullException(nameof(sentence));
- }
-
-
- }
Into a much terser, uncluttered form,
-
-
- int CountWords(string sentence!)
- {
-
- }
Let the Constructor Do the Work
Let's look at an example of a class with a few private, read-only properties,
- public class Widget
- {
- private readonly int _foo;
- private readonly WidgetConfiguration _config;
-
- public Widget(int foo, WidgetConfiguration config)
- {
- _foo = foo;
- _config = config;
- }
- }
The proposal would remove the need for the boilerplate field declarations as the constructor itself would handle creating the arguments passed in,
- public class Widget
- {
- public Widget(int _foo, WidgetConfiguration _config)
- {
-
-
-
- }
- }
If That Wasn't Simple Enough... How About Records?
If you enjoyed the last proposed feature, then you may find yourself doing a
double-take with this one. Another proposal that has been toyed with for a while is the concept of records. Records are a simplified form for declaring classes and structs that supports some new features to simplify working with them (such as caller-receiver parameters and with expressions).
- public class Widget
- {
-
- public readonly string Foo;
- public readonly string Bar;
-
-
-
- public Widget With(string foo = this.Foo, string bar = this.Bar) => new Widget(foo, bar);
- }
We can see this demonstrated below,
- var existingWidget = new Widget("foo", "bar");
-
-
- var clonedWidget = existingWidget.With(bar: "buzz");
-
-
Records will also support the use of positional pattern matching along with the use of destruction in tandem to do things like this,
- var widget = new Widget("foo", "bar");
-
-
- if (widget is Widget("foo", var bar)) {
-
-
- Console.WriteLine(bar);
- }
Switching It Up ... Visual Basic Style?
One of the very few things that you'll see come up from a developer that has recently switched from Visual Basic to C# is the switch statement. Despite C# undergoing nearly nine entire revisions, support for comparison operations within switch statement was never supported (although pattern matching comes close),
this proposal aims to remedy that.
- var internalTemperature = GetInternalTemp(yourDinner);
- switch(internalTemperature)
- {
- case <= 110:
-
- case <= 125:
-
- case 125 to 135:
-
- case <= 145:
-
- case <= 155:
-
- case > 155:
-
- }
But Wait - There's More!
There you'll find tons of additional features that weren't mentioned here such as,
Finally - the C# language is open-source and is always looking for contributors, opinions, and any folks that love the language to share their thoughts on what could make it better.