Oh, get ready for some good news! Microsoft’s gone and added a bunch of shiny new features to C# 12 Preview. In this post, we’re gonna dive right into these cool new features. So, let’s go!
Primary constructors for non-record classes and structs
This is the first feature of the C# 12 Preview. This feature can help reduce boilerplate code and make it easier to initialize properties and use constructor parameters in methods and accessors.
Want an example? Let’s check this example from Microsoft:
public class Student(int id, string name, IEnumerable<decimal> grades)
{
public Student(int id, string name) : this(id, name, Enumerable.Empty<decimal>()) { }
public int Id => id;
public string Name { get; set; } = name.Trim();
public decimal GPA => grades.Any() ? grades.Average() : 4.0m;
}
In the Student
class above, the primary constructor parameters are available throughout the body of the class.
For example, we could do this with Name
(set by name.Trim()
). Additionally, the grades
parameter is used in the property accessor for GPA
.
So, you know how properties aren’t made automatically for primary constructor parameters in non-record classes and structs? Well, turns out you’ve gotta make ’em yourself to show which data is out in the open. That’s pretty much in line with how classes are usually used, right?
Now, here’s the cool part: primary constructors can be a total lifesaver when it comes to dodging the hassle of declaring private fields and, you know, linking parameter values to those fields in those boring constructor bodies.
Wanna know how to pull it off? Just throw in a this(…)
initializer to ring up another constructor for the same class or struct, making sure the primary constructor gets in on the action.
Using directives for additional types
This new feature part of C# 12 is not limited to non-nullable value types and also includes tuples with element types or names.
Here’s an example of how to use aliases for types:
using Measurement = (string Units, int Distance);
using PathOfPoints = int[];
using DatabaseInt = int?;
In the example above, we’re creating aliases for a tuple, an array of integers, and a nullable integer.
You can use these aliases in place of the original type, anywhere that type would normally be used:
public void CalculateDistance(PathOfPoints points)
{ }
Default values for lambda expressions
This feature is similar to default values for regular method parameters, and it allows developers to create more flexible and concise lambda expressions.
Just add an equals sign and the value you want to assign to the parameter after its declaration.
For example, (int addTo = 2) => addTo + 1
sets a default value of 2
for the addTo
parameter, which will be used if no value is provided when the lambda expression is called.
var addWithDefault = (int addTo = 2) => addTo + 1;
addWithDefault(); // 3
addWithDefault(5); // 6
With this, you won’t have to rely on the System’s DefaultParameterValue
. To make the code more succinct and readable, use the InteropServices namespace to establish default values for lambda expression parameters.
Before C# 12, developers had to use a local function or the DefaultParameterValue
attribute to specify default parameter values for lambda expressions. This approach was more cumbersome and less intuitive than the new syntax.
It allows developers to write more concise and readable code, as they can provide default values for commonly used parameters.
It also helps avoid the repetition of similar lambda expressions with only slight variations in parameter values.
Finally, default parameter values for lambda expressions are included in metadata and are available via reflection. This makes it easier for developers to write code that uses lambda expressions dynamically.
Conclusion
In this article, we’ve explored some of the new features that Microsoft has introduced in C# 12 Preview..
Microsoft is continuously working on improving the C# language to make it more efficient and developer-friendly. Developers can expect to see even more advancements in the platform in the near future.