Last Updated on November 19, 2024 by Aram
So C# 13 is now generally available, completely released and ready for production with full support from Microsoft and the community, this article we will be exploring the top 5 features in C# 13.
Lots of great features and updates came bundled with the release of .NET 9
All announced during the 3-days Dotnet Conf 2024 – the biggest online .NET event, that happened just a few days ago ( 12-14, November, 2024).
If you couldn’t catch up with the updates from Dotnet Conf, this article will get you covered with the top 5 features of C# 13 and some code examples with each feature to help you learn about the latest and greatest language C# 13.
You can start using these features right away, just make sure to update your IDE to the latest version, and specifically if you are using Visual Studio 2022, either update to the latest version or make sure your version is 17.12 or above.
So what are the top 5 features in C# 13?
- Params Collections
- New Lock Type
- New Escape Sequence \e
- Init array with index operator ^ (from the end)
- Relaxed constraints on ref struct and ref variables
So let’s dive-in each of these features.
1. Params Collections
Starting C# 13, you can use any collection type when defining your method with the params keyword as one of its input parameters.
This features allows you to pass params values with types other than an array.
You can pass implementations of IEnumerable<T>, Span<t>, ReadOnlySpan<T>
And Interfaces of IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>
The addition of Span and ReadOnlySpan is particularly useful to help the compiler reduce the overhead in memory consumption, since in that case, the array will be stored in stack rather than getting allocated in heap.
Also if you are aware of the features released in C# 12, you would be familiar with using the collection expressions, which would be a great combination when used alongside the params keyword in a function.
Here is an example of how you can use the updated params keyword with collections:
1 2 3 4 5 6 7 8 9 10 11 12 |
void PrintComposers(params IEnumerable<string> composers) { Console.WriteLine(string.Join(", ", composers)); } // Example 1: using a comma separated form to pass the items PrintComposers("Bach", "Handel", "Purcell"); // Example 2: using the collection expressions PrintComposers(["Mozart", "Beethoven", "Chopin"]); // Output: Bach, Handel, Purcell // Output: Mozart, Beethoven, Chopin |
2. New Lock Type
In the System.Threading
namespace a new Lock type has been introduced, it has been designed to replace the traditional lock
statement that relies on object.
This new Lock
class improves code readability, enhances thread synchronization, and offers additional methods for greater control over lock behavior.
Key features include:
Structured and flexible thread synchronization for better safety and readability.
New methods to provide a better control on the code block that requires thread sync:
Enter()
andExit()
for manual lock management.TryEnter()
to attempt immediate lock acquisition.EnterScope()
for cleaner lock handling withusing
.IsHeldByCurrentThread
checks if the current thread owns the lock.
Here is an example of how to use the new Lock type to guarantee a proper synchronization of the different threads when accessing a single i/o resource
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
using System; using System.IO; using System.Threading; class Logger { private readonly Lock _lock = new(); private readonly string _logFilePath; public Logger(string logFilePath) { _logFilePath = logFilePath; } public void Log(string message) { // Enter the lock scope using (_lock.EnterScope()) { // Critical section: write the log to the file File.AppendAllText(_logFilePath, $"{DateTime.Now}: {message}{Environment.NewLine}"); } } } // in program.cs var logger = new Logger("log.txt"); // Simulate multiple threads logging messages Parallel.For(0, 10, i => { logger.Log($"Log entry from thread {i}"); }); Console.WriteLine("Logging complete. Check the log.txt file."); |
The code will write a log.txt file to the output directory of the running application, in this case under the bin\debug\net9.0 folder:
3. New Escape Sequence \e
In C# 13, the \e
escape sequence was introduced as a shorthand for the ESC (escape) character with the Unicode code point U+001B
.
Previously, the ESC character could only be represented using longer sequences like \u001b
or \x1b
.
Therefore, the new \e
escape sequence makes your code more simple and avoids any confusion that might arise when using hexadecimal sequences where subsequent characters might be incorrectly interpreted.
Use cases for the new \e escape sequence is when working with text processing and encoding, command lines, command-based communications in systems like label printers, and others.
To see the \e
escape sequence in action and its output in the command line, we can use ANSI escape codes to control text formatting.
For instance, you can use \e
to change the text color.
See the example below for details, I will share a screenshot from the output to better visualize it:
1 2 3 4 5 6 7 8 9 10 |
string redText = "\e[31mThis is red text\e[0m"; Console.WriteLine(redText); // Example for bold text string boldText = "\e[1mThis is bold text\e[0m"; Console.WriteLine(boldText); // Reset the text style to normal Console.WriteLine("Normal text after the escape sequences."); |
4. Init array with index operator ^ (from the end)
You can now initialize an array using the index operator ^, or better known as the “index from the end” operator.
In versions before C# 13, the ^
operator can’t be used in an object initializer, you need to index the elements from the front.
This feature is particularly useful when initializing collections or arrays where you want to avoid the need to calculate explicit indices manually.
In the below example, you will see how we are initializing an array inside an object with the use of the from the end index operator ^ within an object initializer:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
var playlist = new Playlist() { Tracks = { [^1] = "J. S. Bach - Prelude and Fugue in C minor, BWV 847", [^2] = "Händel: Water Music Suite No. 1 in F major, HWV 348", [^3] = "Vivaldi: Concerto for 2 Violins in A minor, RV 522", } }; foreach (var track in playlist.Tracks) { Console.WriteLine(track); } public class Playlist { public string[] Tracks { get; set; } = new string[3]; } // Output: // Vivaldi: Concerto for 2 Violins in A minor, RV 522 // Händel: Water Music Suite No. 1 in F major, HWV 348 // J. S. Bach - Prelude and Fugue in C minor, BWV 847 |
5. Relaxed constraints on ref struct and ref variables
C# 13 allows ref
struct
as type parameters in generic methods, improving their flexibility in performance-critical scenarios.
This feature ensures that high-performance types like spans can be used with generics safely.
See the example below of being able to use a integer span type with the size of 5 in generics which is allowed after specifying the generic anti-constraint ‘allows ref struct’
1 2 3 4 5 6 |
T Identity<T>(T item) where T : allows ref struct { return item; } var span = Identity(new Span<int>(new int[5])); |
Also you can now declare ref
local variables in async
and iterator methods.
See the example below to understand better:
1 2 3 4 5 6 7 8 9 10 11 12 |
await ProcessDataAsync(); async Task ProcessDataAsync() { int value = 10; ref int refValue = ref value; Console.WriteLine(refValue); // Allowed now await Task.Delay(100); // Cannot reference refValue after this } //Output: 10 |
More features?
Partial Properties
Partial methods introduced in earlier versions now extend to properties, simplifying APIs generated by source generators and enhancing the language’s flexibility.
1 2 3 4 5 |
partial class Example { [GeneratedRegex("pattern")] private static partial Regex MyRegex { get; } } |
Natural Type Refinements for Method Groups
The compiler has improved its ability to infer the type of method groups, leading to fewer ambiguities and reduced errors when working with method delegates.
1 2 3 4 |
void LogMessage(string message) => Console.WriteLine(message); var logger = LogMessage; // Now resolves more naturally to Action<string> logger("Hello, world!"); |
Summary
These have been the key features introduced in C# 13 bundled with .NET 9
- Params Collections
- New Lock Type
- New Escape Sequence \e
- Init array with index operator ^ (from the end)
- Relaxed constraints on ref struct and ref variables
Lots of important and great updates were introduced to the landscape of .NET 9 and its amazing technologies, it will require a good time to explore all these nice changes on the different technologies.
This article covered the new features in C# 13, I will try to cover more technologies in upcoming articles to help you learn more about the modern .NET world and keep you up-to-date with the latest technologies by Microsoft and the community.
Let us know in the comments section your experience so far with .NET 9 and C# 13.
Also if you attended the Dotnet Conf 2024, what was your main area of interest and what was your favorite session?
Feel free to share your experience in the comments section.
You can check the hashtag: #dotnetconf for the latest posts, follow-ups on the biggest online .NET conference.
References
What’s new in C# 12
https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-12
Take a look on what’s new for .NET 9, which includes these amazing changes as part of C# 13
https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-9/overview
Dotnet Conf 2024 was concluded this past weekend (12-14, November, 2024), if you’ve missed it, you can watch it again here:
Try it yourself and start writing projects with latest and greatest .NET 9 and C# 13:
Collaboration and support
I am always open to discuss any potential opportunities and collaborations. Check this page to learn more about how we can benefit each other Sponsorships and Collaborations.
Join 33K+ amazing followers on my LinkedIn to get the latest tips in .NET and C#
If you think my articles are helpful, you can support me by Buying me a coffee
Bonus
Enjoy this brilliant music by Antonio Vivaldi, one of the greatest Italian Baroque compositors and violinists.
Concerto in A Minor for two violins, RV 522 – Antonio Vivaldi