preamble
It is expected that in November 2024, C# 13 will be officially released along with .NET 9. This year's C# updates focus onref struct
Many improvements have been made and many handy features have been added to help further increase productivity.
This article describes features that are expected to be added to C# 13.
Note: C# 13 is not yet officially released, so the following is subject to change.
Use in iterators and asynchronous methodsref
cap (a poem)ref struct
When programming in C#, do you often use theref
variables andSpan
et al. (and other authors)ref struct
types? However, these can't be used in iterators and asynchronous methods, and so localized functions and the like must be used to avoid using them directly in iterators and asynchronous methodsref
variantref struct
type, which is very inconvenient.
This shortcoming was ameliorated in C# 13, where iterators and asynchronous methods can now also use theref
cap (a poem)ref struct
Up!
In iterators, use theref
cap (a poem)ref struct
The example of the
IEnumerable<float> GetFloatNumberFromIntArray(int[] array)
{
for (int i = 0; i < ; i++)
{
Span<int> span = ();
// Do some processing....
ref float v = ref <int, float>(ref array[i]);
yield return v;
}
}
In asynchronous methods using theref struct
The example of the
async Task ProcessDataAsync(int[] array)
{
Span<int> span = ();
// Do some processing....
ref int element = ref span[42];
element++;
await ();
}
I've used the inappropriate and ambiguous "some processing" in order to demonstrate the functionality, but the important thing is that it's now possible to use theref
cap (a poem)ref struct
Up!
There is, however, one caveat.ref
variables andref struct
Variables of type can't go beyond theyield
cap (a poem)await
boundaries are used. For example, the following example will result in a compilation error.
async Task ProcessDataAsync(int[] array)
{
Span<int> span = ();
// Do some processing...
ref int element = ref span[42];
element++; await (); // Do some processing...
await (); element++; // Error.
element++; // Error: access to element is out of bounds for await
}
Although we've talked about it, I think there may be some people who are wondering what the hellref
cap (a poem)ref struct
What it is, so I'll explain a little bit.
In C#, you can use theref
to get a reference to the variable. This allows the original variable to be changed by reference. The following is an example:
void Swap(ref int a, ref int b) // ref denotes a reference
int temp = a; ref
int temp = a; a = b; ref
a = b; b = temp; // Here, a and b have been swapped.
b = temp; // Here, a and b have been swapped.
}
int x = 1; int y = 2; // here a and b have been exchanged }
int y = 2; Swap(ref x, ref y); // Get references to x and y.
Swap(ref x, ref y); // get references to x and y, call Swap to swap x and y
On the other hand.ref struct
is used to define value types that can only exist on the stack. This is to avoid the overhead of garbage collection. However, since theref struct
can only exist on the stack, so prior to C# 13 it couldn't be used in places like iterators and asynchronous methods.
By the way.ref struct
The reason why it comes withref
This is becauseref struct
instances can only exist on the stack, which follows the same life cycle rules as theref
The variables are identical.
allows ref struct
generalized constraint
In the old days.ref struct
cannot be used as a generic type parameter, so a generic type was introduced with code reusability in mind, but ultimatelyref struct
Cannot be used, must beSpan
maybeReadOnlySpan
Rewriting the same processing was then cumbersome.
In C# 13, generic types can also be used with theref struct
Up:
using System;
using ;
Process([1, 2, 3, 4], Sum); // 10
Process([1, 2, 3, 4], Multiply); // 24
T Process<T>(ReadOnlySpan<T> span, Func<ReadOnlySpan<T>, T> method)
{
return method(span);
}
T Sum<T>(ReadOnlySpan<T> span) where T : INumberBase<T>
{
T result = ;
foreach (T value in span)
{
result += value;
}
return result;
}
T Multiply<T>(ReadOnlySpan<T> span) where T : INumberBase<T>
{
T result = ;
foreach (T value in span)
{
result *= value;
}
return result;
}
Why is something likeReadOnlySpan<T>
this kind ofref struct
The type can be used as aFunc
What about the type parameter of the . To investigate this, I looked at the .source code (computing)DiscoverFunc
The generic parameters of a type are defined like this:
public delegate TResult Func<in T, out TResult>(T arg)
where T : allows ref struct
where TResult : allows ref struct;
If you add a generic parameter to theallow ref struct
constraints, then theref struct
The type is passed to this parameter.
It's really a handy feature.
ref struct
You can also implement the interface
In C# 13.ref struct
Interfaces can be implemented.
If this feature is combined with theallows ref struct
In combination, then references can also be passed through generic types:
using System;
using ;
int a = 10;
// utilization Ref<int> save (a file etc) (computing) a references
Ref<int> aRef = new Ref<int>(ref a);
// pass on to sb else Ref<int>
Increase<Ref<int>, int>(aRef);
(a); // 11
void Increase<T, U>(T data) where T : IRef<U>, allows ref struct where U : INumberBase<U>
{
ref U value = ref ();
value++;
}
interface IRef<T>
{
ref T GetRef();
}
// because of Ref<T> this kind of ref struct implementation interface
ref struct Ref<T> : IRef<T>
{
private ref T _value;
public Ref(ref T value)
{
_value = ref value;
}
public ref T GetRef()
{
return ref _value;
}
}
In this way, writingref struct
related code becomes much easier. Also, it is possible to give variousref struct
Implemented enumerator implementationsIEnumerator
and such interfaces too.
The set type andSpan
It is also possible to use theparams
In the old days.params
can only be used for array types, but as of C# 13, it can also be used for other collection types andSpan
。
params
is a function that allows any number of parameters to be specified directly when calling a method.
For example.
Test(1, 2, 3, 4, 5, 6);
void Test(params int[] values) { }
As shown above, it is straightforward to specify any number ofint
Parameters.
As of C# 13, collection types other than array types,Span
、ReadOnlySpan
types and interfaces related to collections can also be addedparams
:
Test(1, 2, 3, 4, 5, 6);
void Test(params ReadOnlySpan<int> values) { }
// or
Test(1, 2, 3, 4, 5, 6);
void Test(params List<int> values) { }
// The interface can also
Test(1, 2, 3, 4, 5, 6);
void Test(params IEnumerable<int> values) { }
That's handy, too!
field
Keywords.
When implementing properties in C#, it is often necessary to define a bunch of fields as follows...
partial class ViewModel : INotifyPropertyChanged
{
// Defining Fields
private int _myProperty;
public int MyProperty
{
get => _myProperty;
set
{
if (_myProperty != value)
{
_myProperty = value;
OnPropertyChanged();
}
}
}
}
So, starting with C# 13.field
Keywords will come in handy!
partial class ViewModel : INotifyPropertyChanged
{
public int MyProperty
{
// Simply use the field
get => field;
set
{
if (field != value)
{
field = value;
OnPropertyChanged();
}
}
}
}
No more defining your own fields, just use thefield
keyword, the field is automatically generated.
This is also very convenient!
Partial properties
One of the common problems when writing C# is that properties cannot be added to thepartial
Modifiers.
In C#, you can add a class or method to thepartial
in order to declare and implement them separately. In addition, it is possible to decentralize the parts of the class. Its main use is to specify what to generate when using automatic generation tools such as source code generators.
Example:
partial class ViewModel
{
// Only methods are declared here, the implementation is automatically generated by the tool.
partial void OnPropertyChanged(string propertyName); }
}
The autogeneration tool then generates the following code:
partial class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
partial void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new(propertyName));
}
}
The developer only needs to declare theOnPropertyChanged
The implementation will be fully automated, thus saving the developer's time.
Starting with C# 13, attributes also supportpartial
:
partial class ViewModel
{
// Declaring Partial Properties
public partial int MyProperty { get; set; }
}
partial class ViewModel
{
// Implementation of some of the attributes
public partial int MyProperty
{
get
{
// ...
}
set
{
// ...
}
}
}
In this way, attributes can also be automatically generated by the tool.
lock object (computing)
As we all know.lock
is a function that is used for thread synchronization via a monitor.
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 a lock object. To use this feature, simply use the Just replace the locked object:
using ;
Lock lockObject = new Lock();
lock (lockObject)
{
// critical zone
}
This makes it easy to improve performance.
Tail index in the initializer
indexing operator^
can be used to represent the relative position of the end of a collection. Starting with C# 13, initializers also support this feature:
var x = new Numbers
{
Values =
{
[1] = 111, [^1] = 999 // ^1 is the first element from the end.
[^1] = 999 // ^1 is the first element from the end
}
// [1] is 111
// [9] is 999 because Values[9] is the last element
};
class Numbers
class Numbers {
public int[] Values { get; set; } = new int[10]; }
}
ESCAPE character
In Unicode strings, you can use\e
substitute (X for Y, or a number in an algebraic expression)\u001b
cap (a poem)\x1b
。\u001b
、\x1b
cap (a poem)\e
Both represent the ESCAPE character. They are usually used to represent control characters.
-
\u001b
denotes a Unicode escape sequence.\u
The last 4 hexadecimal digits represent the Unicode code point. -
\x1b
represents a hexadecimal escape sequence.\x
The last 2 hexadecimal digits represent the ASCII code. -
\e
Indicates the ESCAPE character itself
Recommended\e
The reason for this is that confusion in hexadecimal can be avoided.
For example, if\x1b
Followed by.3
is changed to\x1b3
As a result of\x1b
cap (a poem)3
There is no clear separation between them, so it is not clear that they should be interpreted as separate\x1b
cap (a poem)3
, or put together and explained.
If you use the\e
Instead, confusion can be avoided.
(sth. or sb) else
In addition to the above features, there are some improvements to natural types in method groups and prioritization in method overloading, but they are omitted from this article. If you want to know more, please refer to the documentation.
concluding remarks
C# is evolving year after year, and for me the C# 13 update has implemented a lot of very useful and handy features that solve a lot of real pain points. Looking forward to the official release of .NET 9 and C# 13!