An Exciting Look at C# 12 Latest Features
C# 12 introduces a range of cool new features aimed at simplifying developers’ lives and making the code more readable. These enhancements are designed to streamline coding practices, reduce boilerplate, and improve the overall efficiency of development processes. Let us see some of these latest features of C# 12:
- Primary Constructors
- Collection Expressions
- ref readonly parameters
- Default lambda parameters
- Interceptors
Primary Constructors
Primary Constructors were initially introduced in C# 9 however they were limited to only record types. With C# 12, now you can primary constructors on class as well as struct. To understand this, we must first understand what a primary constructor is. In C#, a primary constructor simplifies class initialization by allowing developers to define parameters directly in the class declaration, which are then automatically assigned to class properties or fields. This feature essentially reduces the boilerplate code making the code more concise and easier to read.
Let us understand with the help of an example. Define the class as shown below
public class Student(string firstName, string lastName, string address, string city, string zipcode)
{
public string FirstName { get; private set; } = firstName;
public string LastName { get; private set; } = lastName;
public string Address { get; private set; } = address;
public string City { get; private set; } = city;
public string Zipcode { get; private set; } = zipcode;
}
Once you define the above class you will receive an error like so:
Click on Show potential fixes and as this is a preview version so you would need to upgrade the version. Once you upgrade your project will build successfully. Initialization of the class would be done in pretty much the same way as before like so:
Student student = new Student("A", "B", "C", "D", "E");
It is important to note once you define the primary constructor your default constructor is lost so you cannot do a new
new Student()
without actually passing in the parameters. If you’d like to however use the default constructor you would have to define in and pass the parameters to our primary constructor like so:
public Student() : this(string.Empty, string.Empty, string.Empty, string.Empty, string.Empty)
{
}
After you define a default constructor passing in parameters to the primary constructor, you could then do a new Student()
Collection expressions
Collection expressions in C# are a powerful feature that significantly enhances the ease and readability of initializing and working with collections such as lists, arrays, and dictionaries. Before the introduction of this feature, you would have to declare a list / array and then write a loop and populate it or you would declare a constructor with passing in more list parameters but now all of that can be done in a single and more streamlined manner.
Let us try to understand with the help of an example of List of List of strings with before vs now:
//Before
List<List<string>> list = new List<List<string>>() {
new List<string>() {"Apple", "Mango", "Chickoo"},
new List<string>() {"Strawberry", "Banana", "Mango"}
};
//Now
List<List<string>> newList = [
["Apple", "Mango", "Chickoo"],
["Strawberry", "Banana", "Mango"]
];
Notice amount of noise that has been reduced from the code. We longer have to write repeated List<string> and the code is extremely easy to read.
Similarly you can also do the same on arrays:
//Before
int[][] arr = new int[][]
{
new int[]{ 1, 2, 3 },
new int[]{ 4, 5, 6 },
new int[]{ 7, 8, 9 }
};
//Now
int[][] newArr = [
[1,2,3],
[4,5,6],
[7,8,9]
];
Going further if you have arrays and you want all of them to be within a new array you would do something like this:
List<string> fruits1 = ["Apple", "Mango", "Chickoo"];
List<string> fruits2 = ["Strawberry", "Banana", "Mango"];
List<string> fruits3 = ["Dragon Fruit", "Cherry", "Orange"];
List<string> fruits4 = [..fruits1, ..fruits2, ..fruits3];
Notice the double .. in the fruits4.
ref readonly parameters
The in
modifier is a feature designed to enhance both performance and safety when working with large structures or data that should not be modified. It is extremely useful in situations where pass by value would lead a significant performance overhead eg. Passing in a large struct. By make it readonly we ensure the function being called does not inadvertently modify its data and thereby create bugs that are difficult to track. Let us understand with the help of an example:
public void SomeMethod(in VeryLargeStruct thing)
{
// thing is readonly inside this method. You cannot do thing.Foo = “bar”
}
The caller has to call this method by using the in keyword:
SomeMethod(in s);
Default lambda parameters
Default lambda parameters introduce a more flexible way of defining lambda expressions by allowing parameters to have default values. With default lambda parameters, we can directly specify the default values right in the lambda’s parameter list. The syntax and the rules are exactly the same as adding default values for arguments a method.
Example:
var function = (decimal price, decimal taxRate, decimal discount = 0) =>
price + (price * taxRate) - discount;
This expression can be called with or without providing a value for the discount parameter. This makes the code more elegant, easy to read and significantly enhances it maintainability.
You can then invoke the function using:
decimal price = function(100, 5);
price = function(100, 5, 50);
Interceptors
Interceptors are a sophisticated programming technique that allow developers to inject additional behavior into method calls or property accesses without modifying the actual code of the methods or classes being intercepted. This is particularly handy for implementing cross-cutting concerns such as logging, authentication, or transaction management in a clean and DRY (Don’t Repeat Yourself) manner. As of today in Feb, 2024, interceptors are still an experimental compiler feature hence it is not advisable to use that in production ready applications.
However, if you’re interested more in learning about interceptors, I highly encourage you to read the interceptor specification.
Searching for the perfect team to hire C# developers? Or perhaps you need dedicated C# developers committed to elevating your project to new heights? WireFuture is your destination. We are a team of experienced professionals and innovative minds dedicated to delivering cutting edge solutions that stand the test of time, focusing on exploiting C#’s strength. Our developers know how to develop solutions that are not only efficient but also scalable and secure, from complex enterprise applications to sleek apps for mobile devices. We at WireFuture understand the unique challenges of your projects, and provide you with personalised attention and devotion that is worthy of your vision. Partner with us, and let’s create exceptional experiences together with the unparalleled expertise of our C# developers.
Success in the digital age requires strategy, and that's WireFuture's forte. We engineer software solutions that align with your business goals, driving growth and innovation.
No commitment required. Whether you’re a charity, business, start-up or you just have an idea – we’re happy to talk through your project.
Embrace a worry-free experience as we proactively update, secure, and optimize your software, enabling you to focus on what matters most – driving innovation and achieving your business goals.