TL;DR: Explore the latest features in C# 13! From enhanced params collections to modernized thread synchronization, discover how these updates can boost your coding efficiency and productivity. Dive in and revolutionize your development experience!
Welcome to our blog about the exciting new features introduced in C# 13! This latest version of C# brings a host of enhancements and innovations designed to empower developers to write cleaner, more efficient, and expressive code.
We’ll delve into the key features and updates that C# 13 offers. From enhanced params collections to the convenience of new escape sequences, C# 13 is packed with tools that make coding more intuitive and productive.
Let’s see them in detail!
Key highlights of C# 13
Following are some of the key highlights of C# 13 :
- Enhanced params collections
- Modernized thread synchronization with lock
- Auto properties with custom logic
- New escape sequence for ESCAPE character
- Implicit index access in object initializers
- Extension for everything: Methods, properties, indexers, and static members
- Optimized method group natural type
Prerequisites
To experiment with these features firsthand, you’ll need the latest version of Visual Studio 2022 or the .NET 9 Preview SDK. Both options provide access to C# 13’s cutting-edge functionalities.
Enhanced params collections
The params keyword was previously restricted to arrays; now, it embraces a wider range of collection types. You can use it with System.Span<T>, System.ReadOnlySpan<T>, and collections implementing System.Collections.Generic.IEnumerable<T> and possessing an Add method. Additionally, interfaces like System.Collections.Generic.IEnumerable<T>, System.Collections.Generic.IReadOnlyCollection<T>, and more can be utilized with params. This flexibility streamlines parameter passing for various collection scenarios.
Support for ReadOnlySpan<T>
In C# 13, the support for ReadOnlySpan<T> has been enhanced to allow collection expressions to work directly with this high-performance struct. ReadOnlySpan<T> is a type-safe and memory-safe read-only representation of a contiguous region of arbitrary memory. It is allocated on the stack and can never escape to the managed heap, which helps avoid allocations and improves performance. Collection expressions can now work directly with ReadOnlySpan<T>, a high-performance struct that avoids allocations. This enhancement is particularly beneficial for applications that require optimal performance.
Benefits of ReadOnlySpan<T>
- Avoids allocations: Since ReadOnlySpan<T> is a stack-allocated struct, it avoids heap allocations, which can benefit performance-critical applications.
- Memory safety: It provides a type-safe way to handle memory, ensuring you do not accidentally modify the underlying data.
- Versatility: ReadOnlySpan<T> can point to managed memory, native memory, or memory managed on the stack, making it versatile for various scenarios.
Consider a scenario where you need to initialize a collection efficiently.
C#
public void AddScores(params int[] scores)
{
var scoresCollection = new int[] { 75, 88, 92 };
AddScores(scoresCollection);
}
In C# 13, you can achieve this with better performance using ReadOnlySpan<T>.
public void AddScores(ReadOnlySpan<int> scores)
{
foreach (var score in scores)
{
// Process scores without allocations
}
}
This is useful for apps that need optimal performance, such as real-time systems, game development, and high-frequency trading applications.
Support for IEnumerable<T>
The params keyword has been enhanced to work with IEnumerable<T>, allowing you to pass collections directly to methods that accept a variable number of arguments. This enhancement improves the flexibility and usability of the params keyword.
Refer to the following code example.
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
// Using params with IEnumerable<T>.
AddItems(new List<int> { 1, 2, 3, 4, 5 });
}
// Method accepting params with IEnumerable<T>.
public static void AddItems(params IEnumerable<int>[] collections)
{
foreach (var collection in collections)
{
foreach (var item in collection)
{
Console.WriteLine(item);
}
}
}
}
Modernized thread synchronization with lock
C# 13 introduces the System.Threading.Lock type, designed to improve thread synchronization practices. It boasts a superior API compared to the traditional System.Threading.Monitor approach.
Key features
- Exclusive execution scope: The Lock.EnterScope() method establishes an exclusive execution scope. This ensures that only one thread executes the code within the scope at a time.
- Dispose of pattern: The returned ref struct from Lock.EnterScope() supports the Dispose() pattern, allowing a graceful exit from the scope. This ensures that the lock is released even if an exception occurs.
- Integration with lock statement: The C# lock statement now recognizes when the target is a lock object and uses the updated API. This integration simplifies the code and improves thread safety.
Benefits
- Improved thread safety: By using the Lock type, developers can achieve better synchronization and avoid common pitfalls associated with thread contention.
- Code maintainability: The new API makes the code more readable and maintainable, reducing the complexity of thread synchronization.
Auto properties with custom logic
Auto properties have been a convenient feature since C# 3, but they have limitations. Specifically, adding custom logic to the getters or setters required reverting to full property syntax, which meant more boilerplate code. C# 13 introduces a significant enhancement to auto-properties, which can now include custom logic directly within their getters and setters.
Let’s explain this with the following code example. Consider a scenario where you want to ensure that a date property is always set to the current date if the provided value is in the past.
using System;
public class Event
{
private DateTime eventDate;
public DateTime EventDate
{
get => eventDate;
set => eventDate = value < DateTime.Now ? DateTime.Now : value;
}
}
public class Program
{
public static void Main()
{
Event myEvent = new Event();
// Setting a past date.
myEvent.EventDate = new DateTime(2020, 1, 1);
Console.WriteLine(myEvent.EventDate); // Outputs current date
// Setting a future date.
myEvent.EventDate = new DateTime(2025, 1, 1);
Console.WriteLine(myEvent.EventDate); // Outputs 2025-01-01
}
}
With the auto property, you can implement custom logic directly within the property definition, reducing the need for backing fields and keeping your code concise and readable.
New escape sequence for ESCAPE character
C# 13 introduces a more convenient way to represent the ESCAPE character ( Unicode U+001B ) within character literals. This new feature allows developers to use the \e escape sequence instead of the older methods, \u001B or \x1B. This enhancement simplifies code readability and reduces potential errors associated with hexadecimal interpretations.
Before C# 13, representing the ESCAPE character required using either the Unicode escape sequence \u001B or the hexadecimal escape sequence \x1B. These methods could be less readable and more prone to errors, especially if the following characters were valid hexadecimal digits.
C#
char escapeChar1 = '\u001B'; // Using Unicode escape sequence.
char escapeChar2 = '\x1B'; // Using hexadecimal escape sequence.
With C# 13, you can now use the \e escape sequence to represent the ESCAPE character. This new method is more intuitive and reduces the likelihood of errors.
C# 13
char escapeChar = '\e'; // Using the new escape sequence.
Benefits
- Improved readability: The \e escape sequence is more concise and easier to read compared to \u001B or \x1B.
- Reduced errors: Using \e minimizes the risk of errors that can occur with hexadecimal interpretations, where subsequent characters might be misinterpreted as part of the escape sequence.
- Consistency: The new escape sequence aligns with other common escape sequences, making the code more consistent and easier to understand.
Implicit index operator in object initializers
C# 13 permits using the implicit “from the end” index operator (^) within object initializer expressions. This lets you initialize arrays directly from the end, as showcased in the following example.
var countdown = new TimerRemaining()
{
buffer =
{
= 0,
= 1,
= 2,
// ... (continues to 9)
}
};
This code snippet initializes an array containing values from 9 down to 0, achieving a countdown effect. Before C# 13, initializing arrays from the end within object initializers wasn’t possible.
Extension for everything: Methods, properties, indexers, and static members
C# 13 expands the concept of extension methods to a new level by including properties, indexers, and static methods. This comprehensive extension mechanism enhances code discoverability and usability, making it easier for developers to extend existing types with additional functionality without modifying the source code.
Extension methods
Extension methods have existed since C# 3. They allow us to add new methods to existing types without altering their definitions. These methods are defined as static methods in static classes, and the “this” keyword specifies the type they extend.
public static class StringExtensions
{
public static bool IsNullOrEmpty(this string str)
{
return string.IsNullOrEmpty(str);
}
}
Extension properties
C# 13 introduces extension properties, enabling developers to add new properties to existing types. This allows for more intuitive and readable code.
In the following code example, the Age property is defined as an extension property for the DateTime type. It calculates the age based on the birth date and the current date. The extension property is defined within a static class DateTimeExtensions. This is a requirement for extension methods and properties.
public static class DateTimeExtensions
{
public static int Age(this DateTime birthDate)
{
var today = DateTime.Today;
int age = today.Year - birthDate.Year;
if (birthDate.Date > today.AddYears(-age)) age--;
return age;
}
}
The “this” keyword before the birthDate parameter indicates that the Age property is an extension property for the DateTime type. This property calculates the age by subtracting the birth year from the current year. It also adjusts the age if the birth date is not yet in the current year.
Extension indexers
With C# 13, you can now define extension indexers, allowing you to add custom indexing behavior to existing types.
In the following code example, we’ve shown how to create an extension indexer for the List<T> type that retrieves elements from the end of the list.
public static class ListExtensions
{
public static T GetFromEnd<T>(this List<T> list, int indexFromEnd)
{
return list[list.Count - indexFromEnd - 1];
}
}
This extension indexer allows you to access elements from the end of the list using a zero-based index, making it easier to work with lists in reverse order. For example, list.GetFromEnd(0) will return the last element, the list.GetFromEnd(1) will return the second-to-last element, and so on.
Extension static members
C# 13 also supports extension static members, enabling the addition of static methods to existing types.
In the following code example, we’ve shown how to create an extension static method for the double type that calculates the square of a given value.
public static class MathExtensions
{
public static double Square(this double value)
{
return value * value;
}
}
This extension static method allows you to call the Square method directly on a double value, making the code more intuitive and readable.
Benefits
- Enhanced code discoverability: By extending existing types with additional functionality, developers can make their code more discoverable and easier to understand.
- Improved usability: Extension properties, indexers, and static members provide a more intuitive way to interact with types, making the code readable and maintainable.
- Separation of concerns: Extension members allow developers to add functionality without modifying the source code, promoting a cleaner separation of concerns.
Optimized method group natural type
C# 13 introduces an optimized approach for method group natural type resolution, refining the overload resolution process involving method groups. This feature enhances performance and aligns more closely with the overall overload resolution algorithm.
In earlier versions of C#, when the compiler encountered a method group, it would generate a complete list of candidate methods for that group. If a “natural type” was required, it was determined based on the entire set of candidate methods. This approach could be inefficient, especially when dealing with large sets of methods or complex generic constraints.
C# 13 streamlines this process by progressively eliminating inapplicable methods at each scope. This includes:
- Scope-by-scope evaluation: The compiler now considers candidate methods scope-by-scope, starting with instance methods and moving to each subsequent scope of extension methods.
-
Pruning inapplicable methods: The compiler prunes methods with no chance of succeeding early in the process. This includes:
- Generic methods with incorrect arity: The methods will be eliminated, if they contain a different number of type arguments than the required.
- Unsatisfied constraints: Generic methods that do not satisfy their constraints are also pruned.
By eliminating inapplicable methods earlier, the compiler reduces the number of candidate methods that need to be considered, improving the efficiency of the overload resolution process.
Conclusion
Thanks for reading! C# 13 delivers compelling enhancements that empower developers to write more streamlined, robust, and expressive code. These features offer significant advantages for various development scenarios, from the versatility of params collections to the improved lock object and the convenience of the new escape sequence.
It continues to evolve by refining existing features and introducing new capabilities that enhance productivity and performance. We encourage you to explore and incorporate these new features into your C# projects to elevate your coding experience. Let’s embrace these exciting changes and continue to build amazing software together. Happy coding!
The latest version of Essential Studio—Syncfusion’s collection of eighteen-hundred-plus UI components and frameworks for mobile, web, and desktop development—is available for current customers from the License and Downloads page. If you are not a Syncfusion customer, you can start a 30-day free trial to try all the components at Syncfusion dot com slash downloads.
If you have questions, contact us through our support forums, support portal, or feedback portal at Syncfusion. We are always happy to assist you!