﻿# What's new in C\# 13: overview

A new C\# version is coming soon, continuing our annual overview series\. This year's release brings more changes than the last\. That's nice to see\! There are both major changes and specialized ones\. Let's take a closer look at them\.

![1173_csharp13/image1.png](https://import.viva64.com/docx/blog/1173_csharp13/image1.png)

## Object initialization using "from the end" index

We'll start our overview with a fairly simple innovation, which is the initialization of an object using the implicit "from the end" index operator \(_^_\)\.

Previously, during initialization, you could specify object indices only "from the beginning":

```cpp
var initialList = Enumerable.Range(1, 10);
var anotherList = new List<int>(initialList)
{
    [9] = 15
};
Console.WriteLine(string.Join(" ", anotherList));
// 1 2 3 4 5 6 7 8 9 15
```

In C\# 13, you can now specify an index by counting elements "from the end":

```cpp
var initialList = Enumerable.Range(1, 10);
var list = new List<int>(initialList)
{
    [^1] = 15
}; 
Console.WriteLine(string.Join(" ", anotherList));
// 1 2 3 4 5 6 7 8 9 15
```

Of course, all classes that implement indexer overloading with the _Index_ structure type argument support the feature:

```cpp
void Check()
{ 
    var initTestCollection = new TestCollection<int>(Enumerable.Range(1, 10));
    var anotherTestCollection = new TestCollection<int>(initTestCollection)
    {
        [^5] = 100
    };
    Console.WriteLine(string.Join(" ", anotherTestCollection));
    // 1 2 3 4 5 100 7 8 9 10
}

class TestCollection<T> : IEnumerable<T>
{
    T[] _items;

    public T this[Index index]
    {
        get =>_items[index];
        set => _items[index] = value;
    }
    // ....
}
```

As mentioned above, the change is simple and straightforward\. The initializer is typically used to define index values sequentially\. A less common practice is to specify the values of particular indices\. Developers will probably use the new object initialization option even less frequently\.

## Partial properties and indexers

The new language version extends the capability to partially declare and implement class members\. Previously, partial declarations were limited to classes, structures, interfaces, and methods\. Now, you can add the _partial_ modifier for properties and indexers\. The logic is standard: you specify the declaration in one part and the implementation in another\.

To give an example, we'll look at the previously declared _TestCollection<T\>_ and tinker with the code a bit:

```cpp
partial class TestCollection<T> : IEnumerable<T>
{
    private T[] _items;

    public partial int Count { get; } // Property declaration
    public partial T this[Index index] { get; set; } // Indexer declaration
    // ....
}
partial class TestCollection<T>
{
    public partial int Count => _items.Length; // Property implementation
    public partial T this[Index index] // Indexer implementation
    {
        get => _items[index];
        set => _items[index] = value;
    }
}
```

Now the class has the declaration of the class indexer and the _Count_ property in one part and the implementation in another\.

It's no secret that programmers use _partial_ classes to generate source code\. For example, they often do it using regular expressions compiled at build time via the _GeneratedRegexAttribute_ attribute; or for data validation when inheriting from the _ObservableValidator_ class\. This small enhancement will help broaden the scope of code generation\. It will also better separate the code declaration and implementation in your large classes\.

## Params collections

It's unbelievable but true\! C\# 13 will introduce the much\-requested \(at least by the author\) support for collections when using the _params_ modifier\. Methods that required us to convert collections to arrays are now easier to work with\. This means less code and better readability\.

Working with databases is a clear example of passing collections to such methods\. We often need to pass a selection obtained using the _LINQ_ _Where_ method, or a list of entry identifiers using _Select_\. This results in the _IEnumerable<T\>_ collection, which needs to be converted to the _T\[\]_ array, because the use of methods with the _params_ modifier in their arguments is limited\. In the upcoming language update, we won't need to do this\. We will be able to write methods that take _params_ collections as arguments without any issues\.

In addition to arrays, _ReadOnlySpan<T\>_, _Span<T\>_, and the children that implement _IEnumerable<T\>_ \(_List<T\>_, _ReadOnlyCollection<T\>_, _ObservableCollection<T\>_, and the like\) will become available for specifying the type with the _params_ keyword\.

Let's take a look at the method call precedence in case of overloads\. Here's an example when multiple overloads of a method are applied and a literal is passed as an argument:

```cpp
void Check()
{
    ParamsMethod(1);
}
void ParamsMethod(params int[] values) // Before C# 13
{
    // (1)
}
void ParamsMethod(params IEnumerable<int> values) // After C# 13
{
    // (2)
}
void ParamsMethod(params Span<int> values) // After C# 13
{
    // (3)
}
void ParamsMethod(params ReadOnlySpan<int> values) // After C# 13
{
    // (4) <=
}
```

Executing the _Check\(\)_ method results in a call to the overload number four, which has _ReadOnlySpan<int\>_ as its _params_ type\. I think it might be due to optimization to avoid additional memory allocation when working with the collection\.

If we restrict the method overloads to _Span<int\>_ and _ReadOnlySpan<int\>_, then passing an array results in the method call with the _Span<int\>_ argument type\. Implicit array conversion to _Span<int\>_ causes such behavior\. If we pass an array to _ParamsMethod_, which is initialized when called, then the _ReadOnlySpan<int\>_ overload is called, since there are no references to the collection in the code:

```cpp
void Check()
{
    int[] array = [1, 2, 3];
    ParamsMethod(array);     // (1)
    ParamsMethod([1, 2, 3]); // (2)
}
void ParamsMethod(params Span<int> values) // <= (1)
{
    // ....
}
void ParamsMethod(params ReadOnlySpan<int> values) // <= (2)
{
    // ....
}
```

When working with template methods, _ReadOnlySpan<T\>_ almost always takes precedence if there's no type\-specific implementation\. Even passing _List<T\>_ results in calling the _ReadOnlySpan<T\>_ params method instead of _IEnumerable<T\>_\. This doesn't seem obvious and is potentially dangerous\.

This feature enhancement is quite significant, even though it may seem minor at first glance\. In projects, we often resort to converting lists to arrays, because most of the time we work with lists or similar data structures\. The use of _ToArray\(\)_ has always seemed redundant, as it's mainly done just to make the code compile without any additional logic\. However, now we can shrug this feeling off and directly pass collections to such methods\.

## Overload resolution priority attribute

A new _System\.Runtime\.CompilerServices_ namespace attribute, the _OverloadResolutionPriorityAttribute_, has been introduced to prefer one overload over another\. The name is cumbersome, but it perfectly captures the point\. As Microsoft explains, the attribute is mainly for API developers who want to "softly" move their users from one method overload to another that may have a better implementation\.

Given the not\-so\-obvious overloading priority chosen by the compiler, we can now explicitly specify which method to use first\. For example, in the context of two _ParamsMethod_ overloads with the _ReadOnlySpan<T\>_ and _Span<T\>_ argument types, we can direct the compiler to prioritize the method with the _Span<T\>_ type:

```cpp
void Check()
{
    int[] array = [1, 2, 3];
    ParamsMethod(array);     // (1)
    ParamsMethod([1, 2, 3]); // (2)
}
[OverloadResolutionPriority(1)]
void ParamsMethod(params Span<int> values) <= (1)(2)
{
    // ....
}
void ParamsMethod(params ReadOnlySpan<int> values)
{
    // ....
}
```

We can see that the attribute takes one value, the overload priority\. The higher the value is, the higher is the method priority\. The default value of each method is 0\.


> \*\*Important note\\\!\*\* If method overloads are in different classes \\\(for example, extension methods\\\), note that prioritization works only within their own classes\\\. That is, a priority from one class of extension methods doesn't affect the other\\\.

The above scope characteristic can be an unexpected problem for a developer\. For example, legacy code with irrelevant logic may cause issues when executed \(especially in a running application\)\. Certain tools that detect such non\-obvious errors can help you prevent this\. Those are static code analyzers\. This update inspired us to add a new diagnostic rule for our C\# [PVS\-Studio](https://pvs-studio.com/en/pvs-studio/) analyzer, in addition to hundreds of existing ones\.

## New Lock class

C\# 13 provides better thread synchronization\. A full\-fledged _Lock_ class from the _System\.Threading_ namespace has replaced _object_\. The class is designed to enhance code readability and efficiency\. In addition to this, the class has the following methods for handling it:

* _Enter\(\)_ enters the locked section\.
* _TryEnter\(\)_ tries to immediately enter the locked section if it's possible\. It returns the result of the entrance attempt in the _bool_ form\.
* _EnterScope\(\)_ gets the Scope structure that can be used with the _using_ statement\.
* _Exit\(\)_ exits from the locked section\.

There's also the _IsHeldByCurrentThread_ property, which helps us see whether the lock is held by the current thread\.

If we use the _lock_ operator in the usual format, the code takes the following form:

```cpp
class LockObjectCheck
{
    Lock _lock = new();

    void Do()
    {
        lock (_lock)
        {
            // ....
        }
    }
}
```

The code example above shows that the new implementation doesn't differ much from the old one\. That's what the code looks when applying the additional features of the new class:

```cpp
class LockObjectCheck
{
    Lock _lock = new();

    void Do()
    {
        _lock.Enter();
        try
        {
            // ....
        }
        // ....
        finally
        {
            if (_lock.IsHeldByCurrentThread)
                _lock.Exit();
        }
    }
}
```

As mentioned earlier, we can utilize an instance of the _Scope_ structure obtained by calling the _EnterScope_ method to ensure that the _IDisposable_ instance is used correctly\. In such a case, the code looks as follows:

```cpp
using (var scope = _lock.EnterScope())
{
    // ....
}
```

The introduction of the code\-locking class brings clarity for beginners and enhanced control for experienced C\# programmers\. The transition to the new format is to be easy and painless, as no major refactoring is required\.

## New escape sequence

The language developers introduced a new escape sequence character, _\\e_\. It's designed to replace the existing _\\x1b_\. Using _\\x1b_ isn't recommended because the next characters following 1b may be interpreted as valid hexadecimal values, which makes them part of the escape sequence\. This case will help avoid unexpected situations\.

## Method group natural type enhancements

C\# 13 refines the rules for determining applicable candidate methods when working with method groups and natural types\. Natural types are types defined by the compiler \(such as when using _var_\)\.

Previously, when processing natural types, the compiler would consider each candidate\. However, in the new implementation, the compiler discards those that definitely don't fit \(e\.g\., template methods with constraints\)\. The change is technical but should reduce compiler errors related to method groups\.

## Interfaces and ref struct

A long time ago, C\# 7\.2 introduced _ref struct_\. It's been reworked in the 13th version of the language\. Let's refresh our memory on that: the main construct feature is the exclusive memory allocation on the stack, with no way to move that memory to the managed heap\. We can leverage this to enhance the security and performance of the application \([learn more here](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/ref-struct)\)\. The familiar _Span<T\>_ and its read\-only analogue, _ReadOnlySpan<T\>_, are notable examples of such structures\.

### Inheritance

Up to this point, _ref struct_ had specific constraints, including prohibition of inheritance from interfaces to avoid boxing\. Attempts to perform such inheritance resulted in the _"ref structs cannot implement interfaces"_ error\. This constraint has now been removed, enabling inheritance from interfaces:

```cpp
interface IExample
{
    string Text { get; set; }
}
ref struct RefStructExample : IExample
{
    public string Text { get; set; }
}
```

It's not that simple, though\. When trying to cast a struct instance to an interface, we get the following error: _"Cannot convert type 'RefStructExample' to 'IExample' via a reference conversion, boxing conversion, unboxing conversion, wrapping conversion, or null type conversion"_\. This is one of the new constraints when using _ref struct_, which ensures referential safety\.

### Anti\-constraint for allows ref struct

This anti\-constraint enables the passing of _ref_ structure instances to template methods\. An attempt to pass such a structure in C\# 12 could result in the following message: _"The type 'RefStructExample' may not be a ref struct or a type parameter allowing ref structs in order to use it as parameter 'T' in the generic type or method 'ExampleMethod<T\>\(T\)'"_\. Now, we can add a construct to enable the use of _ref struct_ where method constraints are specified:

```cpp
void Check()
{
    var example = new RefStructExample();
    ExampleMethod(example);
}
void ExampleMethod<T>(T example) where T: IExample, allows ref struct
{
    // ....
}
```

Yes, everything works correctly\. In such a method, we can easily describe all the general logic needed for _IExample_ derivatives, including _RefStructExample_\.


> \*\*Note:\*\* \_allows ref struct\_ is the first anti\\\-constraint\\\. Such constructs used to only prohibit third\\\-party types\\\.

Looking at this update, we can say that the language developers let us have more fun when building hierarchies\. And that's good\! Such inheritance is useful both for combining logic and for adding implementation commitments to inherited entities\.

The introduction of the allows ref struct anti\-constraint marks a shift in how method specifications are defined\. The idea of enabling some features looks interesting and promising\. It will be exciting to see what new types of anti\-constraints the C\# development team will prepare for us\.

## ref and unsafe in async methods and iterators

Continuing the topic of _ref struct_, it's impossible to overlook an improvement that will widen the scope of their use\. In asynchronous methods, we can now declare _ref_ local variables and instances of _ref_ structures\.

For example, when you try to declare an instance of the _ReadOnlySpan<int\>_ structure in an early version of C\#, the compiler issues the following error, _"Parameters or locals of type 'ReadOnlySpan<int\>' cannot be declared in async methods or async lambda expressions"_\. The new language version eliminates this error, but it's important to note that the restriction against having _ref_ in the arguments of such methods still applies\.

Now, developers can introduce local _ref_ variables in iterator methods \(methods that use the _yield_ operator\)\. However, there's a restriction on the output of these variables:

```cpp
IEnumerable<int> TestIterator()
{
    int[] values = [1, 2, 3, 4, 5];

    ref int firstValue = ref values[0];
    ref int lastValue = ref values[values.Length - 1];
    yield return firstValue;
    yield return lastValue;
    // A 'ref' local cannot be preserved across 'await' or 'yield' boundary
}
```

In the new version of the language, iterators and asynchronous methods will support the _unsafe_ modifier, which enables you to perform any pointer operations\. This will require a safe context in iterators for constructs such as _yield return_ and _yield break_\.

## Conclusion

The list of changes in the new C\# version is larger than last year\. Some add features that were impossible before, while others are designed to make developers' lives easier\. The update may not seem significant to a general expert due to its niche innovations, which may not even be noticeable during the development process\.

What do you think of this? Is Microsoft progressing in the evolution of the language, or is it stagnating by unveiling features that for some reason haven't yet been implemented? Share your opinions in the comments\.

For Microsoft's documentation on the changes in C\# 13, [click here](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-13)\. If you'd like to read our overviews on previous C\# updates, here are the articles:

* [What's new in C\# 9: overview](https://pvs-studio.com/en/blog/posts/csharp/0860/)
* [What's new in C\# 10: overview](https://pvs-studio.com/en/blog/posts/csharp/0875/)
* [What's new in C\# 11: overview](https://pvs-studio.com/en/blog/posts/csharp/1002/)
* [What's new in C\# 12: overview](https://pvs-studio.com/en/blog/posts/csharp/1074/)

As per tradition, expect the release in early November\. While you're waiting, why not [subscribe](https://pvs-studio.com/en/subscribe/) to our blog newsletter so you don't miss out on our other theoretical articles?