Introduction
In this article, I will describe each feature in C# NEXT Feature Status and demonstrate them with examples.
The milestones C# 8.1 and C# 8.2 were removed from GitHub, probably to prevent speculation on a release date or which features could be released in the next version. We have seen in the last years a lot of new features. That caused a lot of trouble, and it was a big challenge for the .Net developer team. Most problems came from the legacy code or to keep the compatibility between the existing programming concepts and the new concepts.
C# NEXT, as shown below, contains the candidate features for C# 8.1 .. 8. x. Only if the candidate features in the “master” branch that means the feature will be released in the next version.
![language feature status]()
Caller Expression Attribute
Allows the caller to 'stringify' the expressions passed in at a call site. The constructor of the attribute will take a string argument specifying the name of the argument to stringify.
Example
https://www.c-sharpcorner.com/article/c-sharp-8-features/
Target-typed new-expressions
"var" infers the left side, and this feature allows us to infer the right side.
Example
Point p = new Point(x, y);
ConcurrentDictionary<int, Queue<int>> x = new();
Mads example
Point[] ps = { new Point(1, 4), new Point(3, -2), new Point(9, 5) }; // all Points
Generic attributes
Allows the generic type in the C# ‘Attributes’.
Example
https://www.c-sharpcorner.com/article/c-sharp-8-features/
Default in deconstruction
Allows the following syntax (int i, string s) = default; and (i, s) = default.
Example
int x, string y) = (default, default); // C# 7
(int x, string y) = default; // C# 8.x
Relax ordering of ref and partial modifiers
Allows the partial keyword before ref in the class definition.
Example
public ref partial struct StructName // C# 7
{
// Struct members and methods
}
public partial ref struct StructName // C# 8.x
{
// Struct members and methods
}
Parameter null-checking
Allows simplifying the standard null validation on parameters by using a small annotation on parameters. This feature belongs to code enhancing.
Example
// Before C# 1..7.x
void DoSomething(string txt)
{
if (txt is null)
{
throw new ArgumentNullException(nameof(txt));
}
// ...
}
// Candidate for C# 8.x
void DoSomething(string txt!)
{
// ...
}
Skip locals init
Allows specifyingSystem.Runtime.CompilerServices.SkipLocalsInitAttribute as a way to tell the compiler to not emit localsinit flag. SkipLocalsInitiAttribute is added to CoreCLR.
The end result of this will be that the locals may not be zero-initialized by the JIT, which is in most cases unobservable in C#. In addition to that stackalloc data will not be zero-initialized. That is definitely observable but also is the most motivating scenario.
Lambda discard parameters
Allow the lambda to have multiple declarations of the parameters named _. In this case the parameters are "discards" and are not usable inside the lambda.
Examples
Func<int, int, int> zero = (_, _) => 0;
(_, _) => 1;
(int _, string _) => 1;
void local(int _, int _);
Attributes on local functions
The idea is to permit attributes to be part of the declaration of a local function.
“From the discussion in LDM today (4/29/2019), this would help with async-iterator local functions that want to use [EnumeratorCancellation].
We should also test other attributes:"
- [DoesNotReturn]
- [DoesNotReturnIf(bool)]
- [Disallow/Allow/Maybe/NotNull]
- [Maybe/NotNullWhen(bool)]
- [Obsolete]
Basic Example
static void Main(string[] args)
{
static bool LocalFunc([NotNull] data)
{
return true;
}
}
The main use case for this feature
Another example to use is with EnumeratorCancellation on the CancellationToken parameter of a local function implementing an async iterator, which is common when implementing query operators.
public static IAsyncEnumerable<T> Where<T>(this IAsyncEnumerable<T> source, Func<T, bool> predicate)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
if (predicate == null)
throw new ArgumentNullException(nameof(predicate));
return Core();
async IAsyncEnumerable<T> Core([EnumeratorCancellation] CancellationToken token = default)
{
await foreach (var item in source.WithCancellation(token))
{
if (predicate(item))
{
yield return item;
}
}
}
}
Advanced Example
https://gist.github.com/rynowak/4d4738a57fb482952056ca67573f1d50
Native Ints
Introduces a new set of native types (nint, nuint, nfloat, etc.), the ‘n’ for the native. The design of the new data types is planned to allow a one C# source file to use 32 naturally- or 64-bit storage, depending on the host platform type and the compilation settings.
Example
The native type is depending on the OS,
nint nativeInt = 55; // Takes 4 bytes when compiled in 32-bit host.
nint nativeInt = 55; // Takes 8 bytes when compiled in 64-bit host with x64 compilation settings.
Function pointers
I remember the term function pointer from C/C++. FP is a variable that stores the address of a function that can later be called through that function pointer. function pointer can be invoked and passed arguments just as in a normal function call.
One of the new C# candidate features is called Function Pointers. The C# function pointer allows for the declaration of function pointers using the func* syntax . It is similar to the syntax used by delegate declarations.
Example 1
unsafe class FunctionPointers
{
delegate void DAction(int a);
void Example(DAction del, delegate*<void, int, void> fun)
{
del(1);
fun(1);
}
}
Example 2
class LogFactory
{
public static void Log() { }
}
void* ptr = &LogFactory.Log;
You have read about the candidate features for 8.1 8.2, and 8. x. In the following article, I will evaluate the candidate's features and write about the pros and cons.