Early peek at C# 11 features

Kathleen Dollard

Visual Studio 17.1 (Visual Studio 2022 Update 1) and .NET SDK 6.0.200 include preview features for C# 11! You can update Visual Studio or download the latest .NET SDK to get these features.

Check out the post Visual Studio 2022 17.1 is now available! to find out what’s new in Visual Studio and the post Announcing .NET 7 Preview 1 to learn about more .NET 7 preview features.

Designing C# 11

We love designing and developing in the open! You can find proposals for future C# features and notes from language design meetings in the CSharpLang repo. The main page explains our design process and you can listen to Mads Torgersen on the .NET Community Runtime and Languages Standup where he talks about the design process.

Once work for a feature is planned, work and tracking shifts to the Roslyn repo. You can find the status of upcoming features on the Feature Status page. You can see what we are working on and what’s merged into each preview. You can also look back at previous versions to check out features you may have overlooked.

For this post I’ve distilled these sometimes complex and technical discussions to what each feature means in your code.

We hope you will try out these new preview features and let us know what you think. To try out the C# 11 preview features, create a C# project and set the LangVersion to Preview. Your .csproj file might look like:

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net6.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
        <LangVersion>preview</LangVersion>
    </PropertyGroup>
</Project>

C# 11 Preview: Allow newlines in the “holes” of interpolated strings

Read more about this change in the proposal Remove restriction that interpolations within a non-verbatim interpolated string cannot contain new-lines. #4935

C# supports two styles of interpolated strings: verbatim and non-verbatim interpolated strings ($@"" and $"" respectively). A key difference between these is that a non-verbatim interpolated strings cannot contain newlines in its text segments, and must instead use escapes (like \r\n). A verbatim interpolated string can contain newlines in its text segments, and doesn’t escape newlines or other character (except for “” to escape a quote itself). All of this behavior remains the same.

Previously, these restrictions extended to the holes of non-verbatim interpolated strings. Holes is a shorthand way of saying interpolation expressions and are the portions inside the curly braces that supply runtime values. The holes themselves are not text, and shouldn’t be held to the escaping/newline rules of the interpolated string text segments.

For example, the following would have resulted in a compiler error in C# 10 and is legal in this C# 11 preview:

var v = $"Count ist: { this.Is.Really.Something()
                            .That.I.Should(
                                be + able)[
                                    to.Wrap()] }.";

C# 11 Preview: List patterns

Read more about this change in the proposal List patterns.

The new list pattern allows you to match against lists and arrays. You can match elements and optionally include a slice pattern that matches zero or more elements. Using slice patterns you can discard or capture zero or more elements.

The syntax for list patterns are values surrounded by square brackets and for the slice pattern it is two dots. The slice pattern can be followed by another list pattern, such as the var pattern to capture the contents of the slice.

The pattern [1, 2, .., 10] matches all of the following:

int[] arr1 = { 1, 2, 10 };
int[] arr1 = { 1, 2, 5, 10 };
int[] arr1 = { 1, 2, 5, 6, 7, 8, 9, 10 };

To explore list patterns consider:

public static int CheckSwitch(int[] values)
    => values switch
    {
        [1, 2, .., 10] => 1,
        [1, 2] => 2,
        [1, _] => 3,
        [1, ..] => 4,
        [..] => 50
    };

When it is passed the following arrays, the results are as indicated:

WriteLine(CheckSwitch(new[] { 1, 2, 10 }));          // prints 1
WriteLine(CheckSwitch(new[] { 1, 2, 7, 3, 3, 10 })); // prints 1
WriteLine(CheckSwitch(new[] { 1, 2 }));              // prints 2
WriteLine(CheckSwitch(new[] { 1, 3 }));              // prints 3
WriteLine(CheckSwitch(new[] { 1, 3, 5 }));           // prints 4
WriteLine(CheckSwitch(new[] { 2, 5, 6, 7 }));        // prints 50

You can also capture the results of a slice pattern:

public static string CaptureSlice(int[] values)
    => values switch
    {
        [1, .. var middle, _] => $"Middle {String.Join(", ", middle)}",
        [.. var all] => $"All {String.Join(", ", all)}"
    };

List patterns work with any type that is countable and indexable — which means it has an accessible Length or Count property and with an indexer an int or System.Index parameter. Slice patterns work with any type that is countable and sliceable — which means it has an accessible indexer that takes a Range as an argument or has an accessible Slice method with two int parameters.

We’re considering adding support for list patterns on IEnumerable types. If you have a chance to play with this feature, let us know your thoughts on it.

C# 11 Preview: Parameter null-checking

Read more about this change in the proposal Parameter null checking.

We are putting this feature into this early preview to ensure we have time to get feedback. There have been discussions on a very succinct syntax vs. a more verbose one. We want to get customer feedback and from users that have had a chance to experiment with this feature.

It is quite common to validate whether method arguments are null with variations of boilerplate code like:

public static void M(string s)
{
    if (s is null)
    {
        throw new ArgumentNullException(nameof(s));
    }
    // Body of the method
}

With Parameter null checking, you can abbreviate your intent by adding !! to the parameter name:

public static void M(string s!!)
{
    // Body of the method
}

Code will be generated to perform the null check. The generated null check will execute before any of the code within the method. For constructors, the null check occurs before field initialization, calls to base constructors, and calls to this constructors.

This features is independent of Nullable Reference Types (NRT), although they work well together. NRT helps you know at design time whether a null is possible. Parameter null-checking makes it easier to check at runtime whether nulls have been passed to your code. This is particularly important when your code is interacting with external code that might not have NRT enabled.

The check is equivalent if (param is null) throw new ArgumentNullException(...). When multiple parameters contain the !! operator then the checks will occur in the same order as the parameters are declared.

There are a few guidelines limiting where !! can be used:

  • Null-checks can only be applied to parameters when there is an implementation. For example, an abstract method parameter cannot use !!. Other cases where it cannot be used include:
    • extern method parameters.
    • Delegate parameters.
    • Interface method parameters when the method is not a Default Interface Method (DIM).
  • Null checking can only be applied to parameters that can be checked.

An example of scenarios that are excluded based on the second rule are discards and out parameters. Null-checking can be done on ref and in parameters.

Null-checking is allowed on indexer parameters, and the check is added to the get and set accessor. For example:

public string this[string key!!] { get { ... } set { ... } }

Null-checks can be used on lambda parameters, whether or not they are surrounded by parentheses:

// An identity lambda which throws on a null input
Func<string, string> s = x!! => x;

async methods can have null-checked parameters. The null check occurs when the method is invoked.

The syntax is also valid on parameters to iterator methods. The null-check will occur when the iterator method is invoked, not when the underlying enumerator is walked. This is true for traditional or async iterators:

class Iterators {
    IEnumerable<char> GetCharacters(string s!!) {
        foreach (var c in s) {
            yield return c;
        }
    }

    void Use() {
        // The invocation of GetCharacters will throw
        IEnumerable<char> e = GetCharacters(null);
    }
}

Interaction with Nullable Reference Types

Any parameter which has a !! operator applied to its name will start with the nullable state being not-null. This is true even if the type of the parameter itself is potentially null. That can occur with an explicitly nullable type, such as say string?, or with an unconstrained type parameter.

When !! syntax on parameters is combined with an explicitly nullable type on the parameter, the compiler will issue a warning:

void WarnCase<T>(
    string? name!!,     // CS8995   Nullable type 'string?' is null-checked and will throw if null. 
    T value1!!        // Okay
)

Constructors

There is a small, but observable change when you change from explicit null-checks in your code to null-checks using the null validation syntax (!!). Your explicit validation occurs after field initializers, base class constructors, and constructors called using this. Null-checks performed with the parameter null-check syntax will occur before any of these execute. Early testers found this order to be helpful and we think it will be very rare that this difference will adversely affect code. But check that it will not impact your program before shifting from explicit null-checks to the new syntax.

Notes on design

You can hear Jared Parsons in the .NET Languages and Runtime Community Standup on Feb. 9th, 2022. This clip starts about 45 minutes into the stream when Jared joins us to talk more about the decisions made to get this feature into preview, and responds to some of the common feedback.

Some folks learned about this feature when they saw PRs using this feature in the .NET Runtime. Other teams at Microsoft provide important dogfooding feedback on C#. It was exciting to learn that the .NET Runtime removed nearly 20,000 lines of code using this new null-check syntax.

The syntax is !! on the parameter name. It is on the name, not the type, because this is a feature of how that specific parameter will be treated in your code. We decided against attributes because of how it would impact code readability and because attributes very rarely impact how your program executes in the way this feature does.

We considered and rejected making a global setting that there would be null-checks on all nullable parameters. Parameter null checking forces a design choice about how null will be handled. There are many methods where a null argument is a valid value. Doing this everywhere a type is not null would be excessive and have a performance impact. It would be extremely difficult to limit only to methods that were vulnerable to nulls (such as public interfaces). We also know from the .NET Runtime work that there are many places the check is not appropriate, so a per parameter opt-out mechanism would be needed. We do not currently think that a global approach to runtime null checks is likely to be appropriate, and if we ever consider a global approach, it would be a different feature.

Summary

Visual Studio 17.1 and .NET SDK 6.0.200 offer an early peek into C# 11. You can play with parameter null-checking, list patterns, and new lines within curly braces (the holes) of interpolated strings.

We hope you’ll check out the C# 11 Preview features by updating Visual Studio or downloading the latest .NET SDK, and then setting the LangVersion to preview.

We look forward to hearing what you think, here or via discussions in the CSharpLang repo on GitHub!

99 comments

Discussion is closed. Login to edit/delete existing comments.

  • kinemaster proapk 0

    Hey All,
    Although I got all the points discussed/shared here but have some confusion in the below points. Can anyone help in the below points:

    void WarnCase(
        string? name!!,     // CS8995   Nullable type 'string?' is null-checked and will throw if null kinemaster. 
        T value1!!        // Okay
    )

    Also, the below class:

    class Iterators {
        IEnumerable GetCharacters(string s!!) {
            foreach (var c in s) {
                yield return c;
            }
        }
    
        void Use() {
            // The invocation of GetCharacters will throw
            IEnumerable e = GetCharacters(null);
        }
    }

    Thanks in advance for the help.

  • Eric Lynch 0

    Regarding null parameter checking, I’m in the camp that doesn’t have much of a problem with the “!!” syntax.

    That said, it might be interesting to implement a more flexible (and powerful) syntax for parameter checking. As other posters have mentioned, another common use case is checking that a string is also not empty. My thought would be to add a generalized syntax that addresses all of the common cases I’ve encountered.

    public int MyMethod(int myParameter throwOn(PredicateMethod[, parm1, ..., parmN]))

    Where the PredicateMethod must be static, must return void, the first parameter is the value to check, the second parameter is the parameter name. Additional optional parameters, limited to constants and literals, might also be supported (e.g. parm1) to handle cases like range checking.

    This single syntax could handle almost any conceivable parameter checking requirements in a relatively concise fashion. And we could provide pre-baked predicate methods for the most common parameter checks. Below are some examples…

    Null Checking

    public int MyMethod(string myParameter throwOn(IsNull)) {}
    
    public static void IsNull<T>(T paramValue, string paramName) where T : class
    {
      if (paramValue == null)
         throw new ArgumentNullException(paramName);
    }
    

    String Checking

    public int MyMethod(string parm1 throwOn(IsNullOrEmpty), string parm2 throwOn(IsNullOrWhitespace)) {}
    
    public static void IsNullOrEmpty(string paramValue, string paramName)
    {
      if (paramValue == null)
         throw new ArgumentNullException(paramName);
    
      if (paramValue.Length == 0)
        throw new ArgumentException(null, paramName);
    }
    
    public static void IsNullOrWhitespace(string paramValue, string paramName)
    {
      if (paramValue == null)
         throw new ArgumentNullException(paramName);
    
      if (string.IsNullOrWhitespace(paramValue))
        throw new ArgumentException(null, paramName);
    }
    

    Limit Checking (using optional parameters)

    public int MyMethod(int parm1 throwOn(IsOutOfRange, 1, 10)) {}
    
    public static T IsOutOfRange<T>(T paramValue, string paramName, T minimum, T maximum)
      where T : IComparable<T> =>
      paramValue.CompareTo(minimum) >= 0 && paramValue.CompareTo(maximum) <= 0 ? paramValue :
      throw new ArgumentOutOfRangeException(paramName, paramValue, null);
    

    Anyhow, that’s my two cents for a comparatively small syntactical change that could support a wide-range of parameter checking…instead of a change that is solely limited to null-checking.

    • Mikolaj Raciborski 0

      unfortunately (for me) this idea is something exactly I was afraid when i read bout the feature.
      And the reason is simple -> why do you need that check in the parameter list? Just why?
      I understand that attribute solution had to be there, because otherwise you didn’t have a place to attach this “behavior”.
      For me, these kind of checks are implementation detail. And now it’s surfacing into interface.

  • László Csöndes 0

    This !! feature is failing a bunch of tests at once that were used to measure how worthy a proposal was and reject other proposals yet it keeps being forced through for some reason.

    Controversial? check
    Narrow scope? check
    “Uses up” core language syntax, and an attribute could easily be used instead? check
    Inflexible and limited? check
    There are existing, convenient solutions for it? check
    Problem could be solved with IDE tooling instead? check

    For people who are concerned with NRT and non-NRT interaction something like <Nullable>checked</Nullable>, or just VS lightbulb->”null check my parameters thanks” would be even less typing.

    • Mikolaj Raciborski 0

      Well said.

  • David S. 0

    I love the idea of parameter null-checking as syntax sugar but I dislike its syntax.

    Nullable reference types feel like something C# 1.0 should have included. Like a revision for an original sin. The vanilla syntax is

    string something

    and I wish for non-nullable in 99% of cases. The abnormal 1% case is

    string? something

    for optional stuff or other use cases. That’s awesome, excatly how I wished it to be!

    Now, parameter null-checking is something I want in 100% of cases for non-nullable stuff. You can keep the ugly syntax

    string something!!

    for developers having NRT disabled. But I have NRT enabled and treated as compiler errors where ever I can (mostly new projects). Of course I would make use of the “sister runtime feature parameter null-checks” if I have NRT enabled.

    I want

    string something

    to be both a non-nullable string and having a parameter null-check without the verbose and ugly !! notation. Can you please add an enum value in the csproj for this case? Something like <nullable>enabledchecked</Nullable> to combine NRT and PNC?

  • Mikolaj Raciborski 0

    Unfortunately this time around i don’t like any proposed feature, here is why:
    1. new lines in interpolated string holes.
    In my opinion this feature will encourage writing ugly code, i would much prefer for developers to use an local variable with the complicated logic they want to put into the “hole” instead of just formatting it slightly better.
    2. list patterns
    It seems to be very useful feature, however it is very confusing one for me. Especially the part of “..” is. From what is see in the example it matches 0 or more. Then the “_” is match exactly any single element ?
    The point is, i was already able to make switch expression that put my peers into deepest part of hell, now we will get some more tools to make it even more magically unreadable and ambiguous.
    still, this feature seems to have the greatest benefits from presented ones anyway.
    3. null checking
    I do like the idea, but i dont like the !!, especially that it will go into pattern matching eventually, hence look back at comment for feature nr 2.
    honestly i am not sure if that should be a part of language feature. I think that having single line check ANE.TIN(object); is fine. When i read code like that i know the intent and the problem and the solution. I can easily understand, when, what and why an exception will be thrown.
    I would probably prefer the string notnull as it’s more human readable, yet I am really thinking why we need these feature.
    Regarding using !! vs ! i sow it’s needed because it will be confused with negation, and guess what, double !! is not less confusing than !.

  • Karl Pickett 0

    The null !! operator seems like a deeper hole in the one already dug by the ! operator which does nothing.

    Have you used swift? Swift got it right — ! will throw an error at runtime, instead of violating the callee’s preconditions. (You are supposed to use a pragma or unsafe keyword if you want to violate safety). Now you want a !! which means no, really, pinky swear, this is really my precondition… I just don’t get it.

    The stewards of the c# language have fallen far from the original tree, I’m afraid.

  • Evgeny Vasin 0

    Hello!
    Can you add readonly modifier for methods?
    In readonly methods we can set readonly fields.
    But readonly methods can call only from constructors or another readonly methods.
    Also in readonly methods we can set init-only properties.

    I think i will be helpfull, because without readonly methods i need to dublicate code in multiply constructors or write a lot of code in one constructor (when i can separate block of code in method), or delete readonly field modifier.

  • 佳佳 王 0

    When is the field keyword available? I’ve been waiting so hard!

  • Andreas Schmitt 0

    I’m going to give my 2 cents on the Null checks.

    Problems:
    a) Exposure of internals:
    First of all, putting this syntax into the method signature is exposing and implementation detail.
    The point to show that the method might throw an ArgumentNullException is in the documentation where it already exists.
    b) Readability:
    In no way does !! suggest a specific exception type at first glance if you’re new to C# or to this feature
    c) Verbosity:
    string? myParameter!! is adding too many symbols
    d) Method body readability:
    Looking just at the method body,which should contain all the implementation details, there’s no place to see that the parameter is null checked.
    Given how some parameter lists are not wrapped depending on code style, this !! might be off-screen or just get missed.
    3) Other solutions already exist

    Advantages:
    a) Saves code, makes a method easier to read. So that’s a positive on the readability side.

    My suggestion: Extend things that are already there. Previously one of the design principles that was voiced by Anders Hejlsberg and others several times was always: Don’t make it a language feature if the runtime can provide a solution.
    A lot of microsoft code uses a ThrowHelper internally. There’s also the .NET Community toolkit now that provides something very similiar.

    https://devblogs.microsoft.com/ifdef-windows/announcing-net-community-toolkit-v8-0-0-preview-1/

    The community toolkit has many advantages. It offers different guard syntax for other types of checking. Array index out of range and similar stuff.

    public static void SampleMethod(int[] array, int index, Span span, string text)
    {
    Guard.IsNotNull(array, nameof(array));
    Guard.HasSizeGreaterThanOrEqualTo(array, 10, nameof(array));
    Guard.IsInRangeFor(index, array, nameof(index));
    Guard.HasSizeLessThanOrEqualTo(array, span, nameof(span));
    Guard.IsNotNullOrEmpty(text, nameof(text));
    // …
    }

    This already saves code and can be shortened even furter. From what I know further compiler support can now make the nameof calls redundant. And equal throw checks could be chained (probably less readobly) or just done with multiple parameters for the same exception. So we could have:

    Guard.IsNotNull(array);
    Guard.AreNotNull(array, text);
    Guard.IsInRangeFor(index, array);

    Not AS much saving as the !! syntax, but parameter checking needs to be somewhat visible and not completely hidden. most Checks are null checks as you said, so could be a single line call to Guard.AreNotNull()
    With proper compiler support if this community toolkit feature moved into the runtime API, the compiler could use the same very well readable auto-generated exception messages that the community toolkit provides for these guards. Their code generators are doing a great job but I bet with some compiler support they could do an even better one, resolving the parameter names and additional context possibly. And with several methods for different parameter checks this can provide a much better and consistent option for parameter checking. After all !! only provides one type of check. Yes it’s the most commonly used one but it’s not the only one. why do one check with syntax and I have to still write the other ones. That introduces inconsistency into C# code. A good, possibly compiler supported, API will do a much better job. It doesn’t introduce implementation details into the method signature, which is messy already since an implementation of an interface will have a different signature from the interface declaration, and instead have it in the method body where it belongs and cannot be missed, but can still be reduced to a single line if all that’s done is null checks. If more than null checks need to be done, then yes we have additional lines, but we’d have those anyway since the feature doesn’s support those. However in that case we’ll have all the checks with a consistent API, and not a single one in syntactic sugar and the rest with API calls. That will confuse people and mess up the consistency of C# usage. If I can do one parameter check with syntax, then why not all? That makes no sense, no matter how common the check is.
    one line of code: Guard.AreNotNull(a, b, c, d, e, f) is not too verbose and still explicit enough not to be missed and can be kept consistent with other types of parameter checking, for which runtime internal code generators and/or the compiler can create very consistent and readable exception messages that contain all the information a developer could need.
    Because what we’re not mentioning here is that yes !! makes the exception throwing short.. but it doesn’t make it easy to write good exception messages. And a proper runtime API support for all kinds of parameter checks where really good messages are auto generated will improve a VASTLY bigger part of C# code out there. Suddenly a ton of code out there will produce much better exception messages on missuse as soon as this API is adopted.
    Microsoft has been using ThrowHelpers and similar API for this purpose internally since forever. Why not make this or ideally the communitytoolkit API for it a core API of the runtime so everybody has a consistent API for these kinds of checks. I’m sure scanning the runtime code itself will show 99.9% of all the commonly used parameter checks and they can then all be done in the same consistent API with consistently good exception messages.
    I think that’s a much better way to solve this issue than !!

    Edit: With compiler support this can also make sure that ALL parameter checks on a constructor happen before field initialization, if necessary, not just one of them

Feedback usabilla icon