preamble
Microsoft is about to release the final version of .NET 9 on November 12, 2024, and .NET 9 Preview 7, released on August 09, is the last preview before the final release. This version will debut with .NET Conf 2024 and has been released with Visual Studio 2022 17.12 Preview 1, which can be installed directly through Visual Studio. NET 9 is also supported by Visual Studio Code and the C# Dev Kit extension.
C# 13, as part of .NET 9, will bring a host of new features to improve development flexibility and performance for a smoother programming experience. Although C# 13 has not been officially released yet, we can try out these new features in .NET 9 Preview 7, which requires downloading the latest preview version of Visual Studio 2022 17.11.
Attention:C# 13 has not yet been officially released, so feature details may be tweaked.
new feature
In C# 13, the use of the params keyword has been extended to apply not just to arrays, but to any recognizable collection type, including the<T>、<T>and the realization of the<T>The type of.
2、Lock object
NET 9 runtime introduces types that provide improved thread synchronization mechanisms. the Lock type supports more efficient thread synchronization operations through its API, such as the ability of the () method to enter an exclusive scope
3. Indexer improvements
The use of indexers becomes more intuitive and flexible, enabling more efficient manipulation of collections.
4. Transitive sequences\e
The advantage of using \e is that it avoids confusion with hexadecimal escape sequences.
5. Some attributes
The introduction of partial attributes allows the definition and implementation of attributes to be distributed across different files, improving the organization and maintainability of the code.
6. Methodology group natural type improvement
The natural types of method groups have been improved, making calls simpler and reducing unnecessary conversions.
7. The use of ref and unsafe in async methods and iterators.
Now that async methods and iterators can use ref variables and unsafe code, it is possible to use these features in more situations, although there are still some limitations.
8, on the extension type (Extension Types) update
A very significant feature in C# 13 allows new methods, properties, and even static members to be added to existing classes without modifying the original class code.
9. New LINQ methodology
New CountBy and AggregateBy methods have been added to allow aggregation of states by key without the need to assign intermediate groupings via GroupBy, which provides a more flexible approach to data aggregation
10, Foreach support Index
Index was introduced<TSource>(IEnumerable<TSource>), which makes it possible to quickly extract the index of an enumerable item in a foreach loop
11. Serialization improvements
NET 9 has been improved with new options for JSON serialization and the introduction of singleton cases that simplify serialization using Web defaults.
12. Performance improvements
NET 9 optimizations in exception handling, loop performance, dynamic PGO (per profile optimization), RyuJIT compiler, and Arm64 instruction set support significantly improve application performance.
Params collection
The params keyword allows a method to accept a list of parameters, which can be anything that implements the IEnumerable<T>The collection type of the interface.
means that collections such as arrays, lists, tuples, etc. can be passed using method parameters without having to explicitly create collection instances.
using System; using ; using ; public class Program { // This method can take any number of string arguments public static void PrintNames(params string[] names) { ("Names provided:"); foreach (var name in names) { (name); } } public static void Main() { // Passing String Parameters Directly PrintNames("Alice", "Bob", "Charlie"); // Using arrays string[] namesArray = new string[] { "Dave", "Eve", "Frank" }; PrintNames(namesArray); // usage list List<string> namesList = new List<string> { "Grace", "Heidi", "Ivan" }; PrintNames(namesList); // Using LINQ Expressions var query = from person in new List<Person> { new Person("Judy", "Walker"), new Person("Kevin", "Smith") } select ; PrintNames(query); // Using properties selected from a collection var persons = new List<Person> { new Person("Leonard", "Nimoy"), new Person("Morgan", "Freeman") }; PrintNames(from p in persons select ); } } public class Person { public string FirstName { get; } public string LastName { get; } public Person(string firstName, string lastName) { FirstName = firstName; LastName = lastName; } }
-
Pass string literals directly.
-
Pass an array of strings.
-
Pass a list of strings.
-
Use LINQ queries to pass query results.
-
Use LINQ to select the FirstName property from a collection of Person objects.
This example demonstrates the flexibility of the params collection, allowing parameters to be passed in a number of different collection types, while the implementation inside the method remains the same.
lock object (computing)
As you know, lock is a feature used for thread synchronization via monitors.
object lockObject = new object(); lock (lockObject) { // critical zone }
However, the overhead of this feature is actually quite high and can affect performance. To solve this problem, C# 13 implements lock objects. To use this feature, simply replace the locked object with a
using ; Lock lockObject = new Lock(); lock (lockObject) { // critical zone }
This makes it easy to improve performance.
Indexer Improvements
Improvements to the indexer, including the ability to use "trailing indexes" (also known as "indexes from the end") in the object initializer.
This type of indexing allows counting from the end of the set, using the ^ symbol to specify the position of the element.
using System; public class Demo { public static void Main() { // Define an indexable type var data = new IndexedData { // Initialize using a traditional indexer Items = { [2] = "Second", [3] = "Third" }, // Initialization with trailing indexes [^1] = "First", // First element from the end [^2] = "Fourth" // Second element from the end }; // Printing initialized data for (int i = 0; i < ; i++) { ($"Index {i}: {[i]}"); } } } public class IndexedData { public string[] Items { get; set; } = new string[5]; }
In this example, the IndexedData class has a string array property named Items.
When initializing the data object, we use two indexing methods:
-
Traditional indexers that initialize array elements by specifying index positions (e.g. [2] and [3]).
-
A trailing indexer that uses the ^ symbol followed by a number to specify the position from the end of the array (e.g.1 cap (a poem)2)。
Index 0: Index 1: Index 2: Second Index 3: Third Index 4: First
Note that the tail index1 is assigned to the last position of the array (index 4), and the2are assigned the penultimate position (index 3), this is because they are counted from the end. This feature is particularly useful when initializing arrays or collections, especially if you need to initialize with the end elements known. Escape sequence \e In Unicode strings, you can use \e to represent the ESCAPE character, which is equivalent to the traditional \u001b or \x1b.
-
\u001b is a Unicode escape sequence where \u followed by four hexadecimal digits represents a Unicode dot.
-
\x1b is a hexadecimal escape sequence where \x followed by two hexadecimal digits represents an ASCII character.
-
\e directly represents the ESCAPE character, which avoids possible confusion.
Partial properties
Prior to C# 13, attributes did not support the use of the partial modifier, which meant that the declaration and implementation of an attribute had to be done in the same location. This can present limitations when automating code generation or separating concerns.
C# 13 improves on this by allowing properties to be declared and implemented across multiple sections. Features are particularly suited to scenarios where they can be used in conjunction with tools such as source code generators, allowing more flexibility in generating and managing attribute code.
public class DemoModel { //Declaring Partial Properties public partial int MyProperty { get; set; } } public class DemoModel { // Implementation of some of the attributes public partial int MyProperty { get { return GetValue(); } set { SetValue(value); } } }
This approach improves development efficiency and reduces repetitive coding efforts by focusing on the business logic portion of an attribute and leaving the specific implementation details to automated tools.
Method group natural type
The natural type improvement for method groups allows the compiler to more accurately determine the natural type of a method, especially when it comes to overload resolution. This means that the compiler can more efficiently recognize which overloaded version should be used, especially in cases involving delegates and method groups.
The following is an example that demonstrates the improvements to the natural types of method groups in C# 13:
using System; public class Program { public static void Main() { // Declare a delegate type that points to a method that takes an Action as a parameter. Action<string> action = PrintMessage; // Calls the PrintMessage method, using the method group as a parameter. action("Hello, World!"); } // This is the original reloaded version public static void PrintMessage(string message) { ($"Original: {message}"); } // C# 13 allows for more precise natural type inference public static void PrintMessage(Action<string> messagePrinter, string message) { messagePrinter(message); ("Improved natural type inference in C# 13."); } }
In this example, the PrintMessage method has two overloads. The first overload accepts a string parameter, while the second overload accepts an Action<string>and a string argument.
Prior to C# 13, if you attempted to call an action delegate using a method group, the compiler could create ambiguity in overload resolution because it needed to determine which overload to use.
The method group natural type improvements in C# 13 allow the compiler to more accurately infer that the first PrintMessage overload should be used because it more closely matches the type of the passed argument (a string). The second overload, while also accepting a string, expects an Action<string>type parameter, which is not matched in method group calls.
Note that this example is only intended to illustrate the concept of natural type improvement for method groups in C# 13. In actual code, method signatures and invocations may need to be adapted on a case-by-case basis.
Prior to C# 13, the ref and unsafe keywords had some limitations in asynchronous methods (those using the async and await modifiers) and iterators.
Here are some examples showing how to use ref and unsafe in asynchronous methods and iterators in C# 13:
1. Use ref in asynchronous methods
async Task RefInAsyncMethod() { int value = 0; await (); ref int local = ref ModifyValue(ref value); local++; // Modify the value of the original variable (value); // Output the modified value } ref int ModifyValue(ref int x) { return ref x; }
In this example, the ModifyValue method returns a reference to the incoming reference. In the asynchronous method RefInAsyncMethod, we use await (); to switch to another context and then modify the value of the original variable with the reference returned by ref.
2、Use ref in iterator
IEnumerable<int> GetNumbers() { int number = 0; yield return number; // Returns the first value number++; // Modify Status yield return number; // Returns the modified value } // Using Iterators foreach (int num in GetNumbers()) { (num); }
In this example, the iterator GetNumbers uses yield return to return the values in the sequence.
The state of the iterator (the number variable) is maintained between yield calls, allowing the modified value to be returned on the second iteration.
3. Use unsafe in asynchronous methods
async Task UnsafeInAsyncMethod() { unsafe { int* p = stackalloc int[10]; for (int i = 0; i < 10; i++) { p[i] = i; } await (); // Switching Contexts // Continue to use p for (int i = 0; i < 10; i++) { (p[i]); } } }
In this example, the unsafe context is used in the asynchronous method UnsafeInAsyncMethod. We use stackalloc to allocate memory on the stack and access this memory before and after await.
This demonstrates that unsafe operations can be performed even in asynchronous methods.
4. Cautions
-
The use of ref and unsafe in asynchronous methods requires caution, as await causes the method's execution context to be hung and resumed, which may affect the expected behavior for ref local variables and unsafe code.
-
Make sure you follow C#'s safety and concurrency rules when using ref and unsafe code.
These improvements in C# 13 provide greater flexibility to use ref and unsafe code in asynchronous programming and iterators, but also require more care to ensure that the code is correct and safe.
summarize
The new features and improvements brought by C# 13, such as the flexibility of extended types, the enhancement of the params keyword, the ability to use ref and unsafe in asynchronous methods, and the optimization of serialization performance, have greatly improved our development efficiency and solved a lot of problems we encountered in real development.
The official release of .NET 9 and C# 13 is awaited with great anticipation, and is believed to bring the community even more powerful and convenient tools to further update and evolve the technology. Download the latest Visual Studio 2022-17.11 preview to experience these new features for yourself.
download address
Download .NET 9.0
Visual Studio 2022 Preview
Reference Links
《C# 13: Explore the latest preview features》
Increasing Productivity in C#: The Complete Guide to C# 13 Updates
ultimate
If you found this article helpful, why not support it with a like? Your support is what keeps me motivated to continue sharing my knowledge. If you have any questions or need further help, please feel free to leave a comment. You can also join the WeChat public number[DotNet Technician] community to share ideas and grow with other tech-loving peers!