August 22nd, 2022

C# 11 preview: generic math, required members, and more

Bill Wagner
C# / .NET Principal Content developer

C# 11 is nearing completion. This post covers features that are new in 17.3 or were not covered in our April update on Visual Studio 17.2 and our February update on Visual Studio 17.1.

The new features in this preview follow on three themes of investment for C# 11:

  • Object initialization improvements: You can support constructors and object initializers in your type easier, independent of the rules you want to enforce for mutable and immutable members. Features include:
    • Required members
    • ref fields
  • Generic math support: You can write algorithms once for multiple numeric types. These features make it easier to use C# and .NET for statistics, machine learning, and other math-intensive applications. Features include:
    • Static abstract and static virtual members in interfaces
    • Relaxed right-shift requirements
    • Unsigned right shift operator
    • Numeric IntPtr]
  • Developer productivity: We’ve added more language features to make you more productive. The extended nameof scope feature is new.

The sections below provide an overview of each feature and links in Microsoft Docs where you can read more. To try these features, you’ll need to enable preview features in your project. That’s explained in the What’s new in C# 11 article in docs.

Improved object initialization

Required members let you write class and struct types that require callers to set certain properties. Consider this Person type:

public class Person
{
    public string FirstName { get; init; }
    public string LastName {get; init; }
}

Callers should use object initializers to set the values of the FirstName and LastName property. But prior to 17.3, the compiler can’t enforce that callers must set those properties. A constructor that requires parameters is the only way to ensure the user sets the FirstName and LastName properties. Required members communicates to the compiler and callers that they must set those properties. Add the required modifier to the member declarations:

public class Person
{
    public required string FirstName { get; init; }
    public required string LastName {get; init; }
}

All callers must include object initializers for the FirstName and LastName properties or the compiler emits an error. The compiler informs callers that required members weren’t initialized. The developer must fix the problem immediately.

If the Person type was written for an earlier release and includes a constructor that sets properties, you can still use required members. You should annotate any existing constructors with the SetsRequiredMembers attribute:

public class Person
{
    public required string FirstName { get; init; }
    public required string LastName {get; init; }

    [SetsRequiredMembers]
    public Person(string firstName, string lastName)
    {
        this.FirstName = firstName;
        this.LastName = lastName;
    }

    public Person() {}
}

The SetsRequiredMembers attribute indicates that a constructor sets all required members. The compiler knows that callers using the Person(string firstName, string lastName) constructor have set the required members. The parameterless constructor doesn’t include that attribute, so callers using that constructor must initialize all required members using object initializers.

The examples above used properties, but you can apply required members to field declarations as well.

This preview also contains an initial implementation of ref fields and scoped values. These changes provide the capability for ref fields in ref struct types. You can also use the scoped keyword to limit the lifetime of ref parameters. The feature proposal and updated changes provide the best documentation on this feature right now. We discovered some scenarios that required language changes to be used safely. The updated changes will be available in a later preview, and the documentation will reflect the final design.

Generic math support

We’ve added features where the motivating scenario was generic math. You’ll only use these features directly in advanced scenarios, such as writing mathematics algorithms that work on multiple number types. Otherwise, you’ll benefit indirectly because the runtime uses these features:

The addition of static abstract and virtual members in interfaces provides much of the important infrastructure for generic math. This feature allows interfaces to declare operators, or other static methods. Classes that Implement an interface must provide the implementation of static abstract methods, just like other methods declared in interfaces. The compiler resolves calls to static methods, including operators, at compile time. There’s no runtime dispatch mechanism as there is with instance methods. The docs provide more details on the specific language rules required to make this feature work.

Other language features smooth out some differences in numeric types to make it easier to write generic mathematics algorithms. The right-shift operator no longer requires the second operand to be an int. Any integral type will do! The nint and nuint types are synonyms for System.IntPtr and System.UIntPtr, respectively. These keywords can be used in place of those types. In fact, new analyzers will gently nudge you to prefer the keywords to the type names. Finally, the unsigned right-shift operator (>>>) avoids casts when you perform an unsigned shift.

Combined, these changes and other changes like checked operators support the generic math runtime changes. The language improvements mean the runtime team can provide improvements across all numeric types in .NET. You can also leverage the features when your types implement contracts using operators, or other static methods.

Developer productivity

The nameof operator now can be used with method parameters. This feature enables you to use the nameof operator in attribute declarations on methods, like the following example shows:

[return: NotNullIfNotNull(nameof(url))]
string? GetTopLevelDomainFromFullUrl(string? url)

Give it a try

Please download the latest Visual Studio 2022 Preview and install the .NET 7 preview or you can separately install latest preview of .NET 7. Once you have it installed, you can try out the new features by creating or opening a C# project and setting the LangVersion to Preview.

This Visual Studio preview gets us closer to the complete feature set for C# 11. We’ve continued to invest across multiple themes in this release. We’ve made corrections along the way based on the feedback you’ve already given us. Now is a great time to download the preview, try all the new features, and give us feedback. We’re listening and making final updates for C# 11 and .NET 7.

Author

Bill Wagner
C# / .NET Principal Content developer

Bill Wagner writes the docs for https://docs.microsoft.com/dotnet/csharp. His team is responsible for all the .NET content on docs.microsoft.com. He's also a member of the C# standardization committee.

68 comments

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

  • Wouter Kettlitz

    Shouldnt it be the other way around? [DoesNotSetRequiredMembers]. I assume a constructor always sets required members and this attribute could be inferred on empty constructors.

  • 状 张

    Hi,Is there still hope for “field”

    class Person
    {
      public string Name { get; init => field = value.Trim(); }
      public DateTime DateOfBirth { get; set => field = value.Date; }
    }
  • Tyler

    The required members thing does not sound good. More useless bloat. No value.

  • Marco Encarnacao

    Feature set on c# should have been closed a long time ago.

  • Marco Encarnacao

    “Improved object initialization”

    You’re just creating non-sense really.

  • Jeff Johnson

    Could we just replace init with required? To me, required implies init…

    • D Kh · Edited

      Second that. There are already too many context aware keywords with very questionable value.
      For example, "required" is only once concern, but next MSFT may vote to add "good" specifier to designate
      assignment that only take "good" data. And yes, one can amass 1000pages proving that there is a way and some value
      in defining "good" vs "bad" data. Purism at its best.

      The problem starts when "buzzword marketing" start making language design decisions.
      As stated...

      Read more
  • Stuart Ballard

    The feature I was looking forward to the most (semi-auto properties) seems to have got cut and moved to the next version, without much notice; other than following the issue on github and someone having actually asked, the only indication has been that posts like these stop mentioning it.

    I know that there are always disclaimers that anything in early development or preview status might not end up in the final release, but I still feel...

    Read more
    • Bill WagnerMicrosoft employee Author

      Hi Stuart,

      I understand your frustration. However, I think that much additional communication will be more noise than signal. I watch https://github.com/dotnet/roslyn/blob/main/docs/Language%20Feature%20Status.md to track when features are likely to make the next release Once the State column says "Merged in ???*, that feature will be available in a public preview. My aspirational goal is to have the docs ready by the public preview. I don't always meet that one, but I get close.

      That's still not a...

      Read more
      • Stuart Ballard

        That's vary fair. I've generally been watching the individual github issues because that way I get actively notified of changes rather than having to periodically check for them, so I was surprised when the target release had changed without even being mentioned in https://github.com/dotnet/csharplang/issues/140 until someone happened to ask. But as far as public discussion I can see the potential for the signal-to-noise ratio not being worth it. Maybe a footnote on posts like this...

        Read more
  • Paulo Pinto

    All in all, very interesting set of features, just SetsRequiredMembers doesn’t feel like C# rather one of those kludges that C++ is known for.

    • D Kh

      Exactly.

  • silkfire · Edited

    I seem to fail to understand the difference:

    <code>

    and

    <code>

    Does the second example mean that if we never e.g. assign in the object initializer (as it's only optional) then we're not going to be able to assign it later?

    Read more
    • Fred SilberbergMicrosoft employee · Edited

      The second example has been legal for two releases now, and means something. With very rare exception, we do not make breaking changes to code. Further, there is no way to determine whether or have been given reasonable defaults by the original type author, as the user could have written this and it would look exactly the same:

      <code>

      Read more
      • Marco Medrano

        Hi @Fred Silberberg, regarding “we do not make breaking changes to code” I rather prefer breaking changes to unreadable and non-intuitive syntaxis.
        If one wants to move from version X to X++, one has to accept not just new features but corrections as well.
        Just letting you as seem you are an active part of the development of this awesome language.

      • Thomas Eyde

        But the compiler checks in a way that defaults are set. Because if you don't, you get the warning "Non-nullable property 'FirstName' is uninitialized. Consider declaring the property as nullable."

        The fix, as we know is to initialize with 'null!'. Which goes against declaring it as a non-nullable in the first place.

        What I fail to understand: When nullable is enabled, which is optional, why can't you postpone that check until first usage or when it goes...

        Read more
    • John King · Edited

      +1

      I think we don’t need required keyword the init + reference nullable should means “required” , what we need is actully


      public string Name {get; required set }
      • D Kh · Edited

        What it means is that it is time to start thinking about getting away from C#.
        The insatiable desire to "add value" by adding features turns languages into a very complex set of "gotchas" that are really not needed.
        The very fact of questions like this arising is a testimony to that.

        As stated before, the "required" means something to "compiler" , but what des it mean for a developer? Absolutely nothing.
        Suppose I set a...

        Read more
      • Marco Medrano

        Sad to read criticism but not proposals.
        The “insatiable desire to add value” makes bigger and better things. We have the opportunity to make it better or run away.

      • Tyler

        Very well said. Thank you.

      • Marco Encarnacao

        +1

      • Jeff Johnson

        +100 to this

      • Fred SilberbergMicrosoft employee

        During the design of the feature, we commonly saw people conflate nullability and requiredness. Unfortunately, this is a mistake: properties can be structs , for example, or you might want to require a nullable property (ie, that null is a valid value to set, but you want your consumer to make that decision intentionally).

      • Thomas Eyde

        Maybe you should have made a poll on how many of us thinks ‘null == optional’ and ‘not-null == required’? If the majority thinks that, then that is what it means.

        I, for one, think logic is off when a required property can be null.

  • Philip Lewis

    Load the ointernet favucon