What’s New in C# 12–2024

C# 12, the latest iteration of Microsoft’s popular programming language, brings a slew of exciting features aimed at enhancing developer productivity, improving code readability, and supporting more robust software development practices. Let’s see what is new in C# 12!
1. Primary Constructors for Classes
One of the most anticipated features in C# 12 is the introduction of primary constructors for classes. This feature, previously available for record types, is now extended to classes, making it easier to define classes with concise syntax.
Old way:
public class Person
{
public string Name { get; }
public int Age { get; }
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
New way using primary constructors:
public class Person(string name, int age)
{
public string Name { get; } = name;
public int Age { get; } = age;
}
A primary constructor allows constructor parameters to be available throughout the class, not just within the constructor, reducing boilerplate code significantly.
public class Person(string firstName, string lastName)
{
public string FullName => $"{firstName} {lastName}";
}
With primary constructors, the syntax is cleaner and more readable, reducing boilerplate code while maintaining full functionality.
2. Collection Expressions
C# 12 introduces collection expressions, a feature that simplifies the creation and manipulation of collections using a more intuitive syntax. This addition allows you to create and work with collections directly within a single expression, making your code more expressive.
Example:
var numbers = [1, 2, 3, 4];
var doubled = [for var n in numbers select n * 2];
This feature resembles list comprehensions in Python and other languages, offering a more concise way to perform common collection operations.
3. Intercepting Parameter Null Checking
C# 12 improves upon the existing null-checking capabilities by introducing interceptors that can handle parameter null checks more efficiently. This feature makes code more robust by centralizing null-checking logic.
Example:
void PrintName(string name!!)
{
Console.WriteLine(name);
}
In this example, the !!
operator intercepts the null check, throwing an exception if name
is null, making the code cleaner and reducing the need for explicit null checks.
4. Default Lambda Expressions
C# 12 allows lambdas to have default parameter values, aligning them more closely with regular methods. This feature increases flexibility when defining inline functions.
Example:
Func<int, int, int> add = (x = 1, y = 2) => x + y;
Console.WriteLine(add()); // Outputs 3
This change brings a new level of versatility to lambdas, enabling developers to write more dynamic and adaptable code.
5. Improved Raw String Literals
C# 12 further refines raw string literals, allowing for more flexible formatting, especially useful when dealing with multi-line strings or strings containing special characters. The updates include the ability to control indentation and escape sequences more effectively.
Example:
string json = """
{
"name": "John",
"age": 30
}
""";
This update makes raw strings more readable and maintains formatting consistency, especially in scenarios involving JSON, XML, or other formatted text.
6. Enhanced Pattern Matching
Pattern matching continues to evolve in C#, and C# 12 introduces even more powerful ways to work with data. Enhancements include new patterns like list patterns, which enable matching and destructuring lists directly.
Example:
int[] numbers = { 1, 2, 3, 4 };
if (numbers is [1, .. var rest])
{
Console.WriteLine($"First is 1, rest: {string.Join(", ", rest)}");
}
These patterns simplify complex matching logic and make the code cleaner and more intuitive.
7. using
Directives Inside Namespaces
To further tidy up code and avoid repetitive using
directives, C# 12 allows using
statements directly inside namespaces. This change helps encapsulate dependencies within a specific scope, reducing global namespace pollution.
Example:
namespace MyNamespace
{
using System;
using System.Collections.Generic;
class MyClass { /* ... */ }
}
This feature is particularly useful in large projects, helping keep code organized and dependencies clear.
8. Inline Arrays and Spans
C# 12 introduces more intuitive support for inline arrays and spans, allowing you to define arrays in place and work with spans directly, making high-performance operations more accessible.
Example:
var numbers = stackalloc int[] { 1, 2, 3 };
ReadOnlySpan<int> span = new[] { 4, 5, 6 };
This feature benefits scenarios requiring high-performance, memory-efficient code, such as real-time processing or system-level programming.
9. Easier Interpolated Strings
Interpolated strings get an upgrade, allowing for more flexible and readable expressions within interpolations. The new syntax simplifies working with strings, particularly in complex formatting scenarios.
Example:
int age = 30;
string message = $"Age is: {age:#00}";
10. Experimental Attribute
C# 12 introduces an experimental attribute that allows marking types or methods as experimental, providing a diagnostic ID that triggers compiler warnings.
Example:
[Experimental("EX001")]
public class ExperimentalClass { /*...*/ }
This attribute helps teams flag features that are still in development or subject to change, providing an additional layer of communication between developers.
11. Alias Any Type
Aliases in C# 12 are no longer restricted to named types; now, you can alias complex types such as tuples and arrays.
Example:
using PersonAlias = (string firstName, string lastName);
PersonAlias person = ("John", "Doe");
Console.WriteLine(person.firstName);
Last Notes
C# 12 is all about reducing boilerplate, improving code clarity, and providing more expressive syntax to solve real-world problems efficiently.