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

Bill Wagner

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.

68 comments

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

  • ifgitman 1

    What about adding record-like syntax for regular classes? Something like this:

    class Person(string lastName, string firstName);

    I think it would be very useful for Dependency Injection, as it is quite common to write constructors just for initializing fields.

    • John King 1

      I’d perfer the Typescript one

      class Persion
      {
          public Persion(private string lastName, public string firstName){}
      }
      
    • Jacob McDowell 0

      IIRC the “primary constructor” proposal would make it like this:

      class Person(string lastName, string firstName) {}

      but i think that proposal is older than records so who knows. 🤔

    • saint4eva 0

      It is called C# primary constructor – it is coming.

  • Ken Hornibrook 0

    I was a bit surprised by the need for [SetsRequiredMembers]. I thought the compiler would be able to figure this out using definite assignment analysis, similar to a deferred initialisation of a method-scoped variable.

    I had the idea that you would be able to provide a range of constructors that might assign some of the required fields, and the caller could add in any missing ones using an object initialiser, per their preference.

    If we are relying on the developer to specify [SetsRequiredMembers] correctly, I could imagine a circumstance where I add a new required property to a class, forget to update the constructor to populate the new property, but the compiler doesn’t pull me up due to [SetsRequiredMembers] being present. It does weaken the type safety that required purports to provide.

    • John King 0

      +1

    • John Tur 0

      The rules for supporting definite assignment in this way would be complicated. A key aspect of this feature is the ability to use it with regular (perhaps existing) class types. Virtual methods and calling constructors from base classes, for example, make definite assignment impossible to always compute accurately for fields. SetsRequiredMembersAttribute is an escape hatch that lets you do anything you want (even something too complex for static analysis to verify).

      The team did consider a more fine-grained version of this feature that would allow writing explicit contracts that were verified by the compiler. The proposal mentions it as a possible future extension:

      NB: An earlier version of this proposal had a larger metalanguage around initialization, allowing adding and removing individual required members from a constructor, as well as validation that the constructor was setting all required members. This was deemed too complex for the initial release, and removed. We can look at adding more complex contracts and modifications as a later feature.

      • Adam Simon 0

        “Virtual methods and calling constructors from base classes, for example, make definite assignment impossible to always compute accurately for fields.”

        The assignment tracking logic doesn’t need to be perfect. It’s enough if it covers the majority of the cases. (As in the case of many other types of static analysis done by the compiler. For example, tracking of uninitialized variables or tracking of variable nullability. Neither of these are perfect but still undoubtedly useful.)

        So I strongly believe that the compiler could and should automatically annotate all the constructors which it can (easily) prove to set all required fields/properties. Think of code like this:

        class Foo
        {
            public Foo(int x, int y)
            {
               X = x;
               Y = y;
            }
        
            public required int X { get; init; }
            public required int Y { get; init; }
        }

        I think like 90% of the cases has this level of complexity or may include a call to the base ctor of similar complexity. I don’t see any inherent difficulty which would prevent the compiler from tracking such cases and automatically emitting that attribute.

        You could still leave the door open for developers to override the default behavior by explicitly adding the annotation when the ctor does something tricky, calls into another initialization method, etc. IMHO this would be a more sane approach as opposed to make it the developer’s responsibility in all cases regardless how obvious it is. Not to mention the unnecessary noise your approach would create in the code.

        Please reconsider this because it doesn’t look like a good (developer-friendly) design choice.

        • Fred SilberbergMicrosoft employee 0

          The assignment tracking logic doesn’t need to be perfect.

          We strongly disagree with this sentiment. Required members are part of your public API: they don’t affect your type, they affect your consumers. There is no case in C# where static analysis affects the exported public API of a type, and for good reason: a refactoring to share common code could potentially cause you to ship a breaking change to consumers without meaning to do so.

          I think like 90% of the cases has this level of complexity or may include a call to the base ctor of similar complexity. I don’t see any inherent difficulty which would prevent the compiler from tracking such cases and automatically emitting that attribute.

          Your base type might not be coming from the current assembly. It could be coming from another assembly, and the compiler has no visibility into the body of constructors defined in another assembly. In fact, the body might even just be

          throw null;

          , if the referenced assembly is actually a ref assembly, and not a real assembly.

      • Michael Taylor 0

        “Virtual methods and calling constructors from base classes, for example, make definite assignment impossible”

        If you’re calling a virtual method in a constructor then you are already in undefined territory. The only scenario would be base constructors. In this case I believe it should follow the rules that we already have today which is that if you call a ctor then assume all required properties are set. Unless you add fine-grain control over which properties (which would be ugly) then it is an all or nothing. And the only scenario that I think would remotely make sense as to not being in this situation is the default constructor.

        So I’d argue that by default no attribute on a ctor should be needed. The compiler should assume a non-default ctor sets the required properties (until such time as more fine grained options are available) and any ctor that doesn’t follow this should be annotated to say it doesn’t set the required members. After all this is sort of designed to eliminate ctors that accept required parameters to work around current language limitations that cannot enforce it. So assuming existing code works that way would be cleaner. Perhaps the default ctor would be the only one that wouldn’t follow this rule but it seems like it would just be easier to assume they all do and “opt out” on those that don’t.

        • Kathleen DollardMicrosoft employee 0

          Required is a way to communicate what must be set during object initialization. We expect it will be common to have constructors that do some part of the initialization work, but still require the user to set some fields. Thus, constructors are not assumed to set all values unless you state that they take this responsibility via the attribute. This also makes it easier to incorporate the new required feature into existing classes.

          • Silvan Kurth 0

            Is it possible to use or at least consume required feature in VB?

            Promises

        • Fred SilberbergMicrosoft employee 0

          So I’d argue that by default no attribute on a ctor should be needed. The compiler should assume a non-default ctor sets the required properties (until such time as more fine grained options are available) and any ctor that doesn’t follow this should be annotated to say it doesn’t set the required members.

          That’s certainly an interesting tack. However, we don’t think that mixed scenarios are completely out of the picture, and adopting this approach would block future work supporting mixed scenarios.

    • Markus Hjärne 0

      +1

    • Hendry Luk 0

      That was my thought too initially, but thinking more about it, I can see why that’s needed.

      At first glance, it looks similar to how Java enforces assignments of all final fields. But this is actually different.
      [SetsRequiredMembers] is an external contract. It’s NOT for validating the code within the constructor or even within the class. Rather it’s to be consumed by external code that depends on this library, therefore it needs to be part of its public API signature, hence the attribute.

      In theory the compiler could automatically insert this attribute at compile time by analysing the body of the constructor, but it’d be a bad design. All parts of method signature must be explicitly declared, not inferred. I.e. altering the body of a method must not silently alter its public signature and thus its external contract.

  • Dan Spiteri 1

    EWWW!! Stop bloating c# already. It was a great language and now you’re adding all this bloat, it’s gross!

    • Nate Barbettini 1

      All of these new features are optional. They help in certain situations, but if they don’t help you, you don’t need to use them.

      I’m personally excited about the `required` keyword because it will remove the need to write constructors just to ensure that objects are initialized with non-null values (e.g., string auto-properties).

      • Max Mustermueller 3

        We don’t need to use them but we have to remember them when we read other’s code. And if you don’t use them, you hardly can remember the meaning of them as you never used them. Required is an easy one since its obvious by its name, but things like ref init record are difficult to understand for people who are learning C# or who aren’t using them every day.

        I for example still have to look up how to write the new switch expression because I find it very unusual to write. And it annoys me that there are two ways to write it. The idea was to make it more easier and simple but personally I ended up with the opposite. Its difficult and more complex for me.

        • Marco Encarnacao 0

          +1

      • Marco Encarnacao 0

        They are optional right. but imagine everyone writing code with optional nonsense?
        I already had to put in place rules to stop people using syntaxes no one can read, when in fact the language should be all about readiness.

    • Martin Enzelsberger 1

      I remember the uproar when var was added. One of the first things that I remember that “destroyed” C#.
      You can choose the version of the language you want to use, you know? Just pick C# 4 and live forever happy in your perfect world, you don’t have to read these mean articles about scary new stuff.

      Keywords like goto, fixed or stackalloc have been in the language forever and many C# devs don’t even know them, or never use them.
      Never heard any of them complain that they’re there. But this new stuff that you don’t have to use if you don’t want to, argh, how it ruins the C# language by invisibly existing.

      • MgSam 1

        This is such an absurd argument. Adding features to any programming language affects all developers using that language, whether they personally use them or not. 90% of coding is reading already-existing code. You need to be able to understand and follow all possible feature usages because many times you’re reading code others have written.

        • D Kh 0

          Exactly so.
          And comparison with “var” is absolutely inappropriate.

          C# is a multi-paradigm language and VAR is a MUST to have feature for dynamic typing, with anonymous objects.
          This is like the bread and butter of functional usage of new{ a=1, b =2}, Tuples etc…

          The “required” as a keyword is a very very questionable feature as it makes absolutely no sense from data validation perspective.
          It does help compilers to init fields, ok, but that means nothing for business logic cases – where 99% of complexity is

          • Marco Encarnacao 0

            -1

      • Marco Encarnacao 0

        -1

    • Steve 0

      I do not see where it bloats. The new features such as static virtual on interfaces and required members are quite essential especially for library authors. For example, if static virtual on interfaces does not exist in C#, we cannot even express type traits, so we have to specialize all types in the code or using reflection for a simple Parse method, and actually this is the real causing of bloating and slow. Now we have the new feature, we can simply make the code simple, less bloating, generic, and high performant.

    • D Kh 0

      Agreed 10000000000%

  • Dmitry 0

    SetsRequiredMembers

    Seems like a very bad solution, why can’t the compiler determine which of the required fields were set in the constructor?

    This feature should reduce the number of bugs in object initialization and simplify it, but with this approach it is possible not to initialize fields and put an attribute (it can easily happen during code evolution), in the constructor only part of required fields can be initialized, what to do in this case?

    • Kathleen DollardMicrosoft employee 0

      We spent considerable design effort having the compiler evaluate what was set. If there was only the simple constructor, that would work well. But, with base class constructors and other complicating scenarios, we wound up creating a weird meta-language around specifying what a constructor did or did not do. That approach seemed wrong, and we really, really wanted to get the simple case solved.

      We might revisit this in the future if there is demand and we have better ideas for solving the difficult problems that arose.

    • Fred SilberbergMicrosoft employee 0

      Seems like a very bad solution, why can’t the compiler determine which of the required fields were set in the constructor?

      It certainly could. However, required members are part of your public API: they don’t affect your type, they affect your consumers. There is no case in C# where static analysis affects the exported public API of a type, and for good reason: a refactoring to share common code could potentially cause you to ship a breaking change to consumers without meaning to do so.

      with this approach it is possible not to initialize fields and put an attribute (it can easily happen during code evolution), in the constructor only part of required fields can be initialized, what to do in this case?

      We expect that future work will make this case better (particularly for mixed scenarios where you might want to initialize a single member and leave the rest for an object initializer). For now, we expect SetsRequiredMembers to be a rarely-used escape hatch, not something that most people need to do.

    • MgSam 0

      Completely agree that the SetsRequiredMembers attribute is a mess of a solution that will lead to bugs. In fact this whole “required” feature is a solution looking in search of a problem. It’s verbose, adds no new expressiveness, and doesn’t cover any more complex validation logic that a type might need. We already have a way to force parameters to be set- they’re called constructors.

      This required feature should just be entirely scrapped.

      • Paulo Pinto 0

        Same feeling here, I really don’t see a need other competing with more hip languages that aren’t pure OOP like how C# was born.

        So we get to retrofit kludges that don’t fit with the language DNA and have to be half solutions.

  • Lee Vincent 0

    Can you tell me what the following code means?

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

    Why not ?

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

      The first snippet just failed to compile. If a property is marked as required, it must be settable or initializable. Also, set and init cannot coexist on the same property so putting required on the property instead of accessor sounds fine to me.

    • Fred SilberbergMicrosoft employee 0

      Fields can also be marked required, so for consistency we wanted the required keyword as a modifier on the member itself.

      • Lee Vincent 0

        C# is OOP,not compiler-oriented programming.
        I don’t want the compiler to teach me how to write code.
        If someone wants required member, they should use property.

        • Fred SilberbergMicrosoft employee 0

          If someone wants required member, they should use property.

          We don’t agree with this. We expect simple interop structs to take advantage of this feature, and they will (must, even) be using fields, not properties.

          • D Kh 0

            This is still senseless from practical point.
            Suppose I have a “required” string field /property whatever…. I set it to EmptyString. Is the contract satisfied?
            Suppose I have an integer value. I set it to zero. Does it pass the check? But what does zero mean?
            Is this a sensible assignment “span = deafult(Timespan);” ? The value of these changes are very questionable.

          • Marco Encarnacao 0

            You’re just help making c# a language people will run away from due to it becoming a non-sense overcomplicated language to read, to the point that using all those optional sytaxes we can actually write code a very senior developer wont be able to read.

        • Marco Medrano 0

          C# is multiparadigm, which makes the difference with Java and other purist languages.
          I think in required as another keyword to inform another developer consuming this code, has to be set. We already did that through constructors, although it was much more code.

    • Jeff Johnson 1

      Better yet, since required implies init:

      public class Person
      {
          public string FirstName { get;  required;}
          public string LastName {get; required;}
      }
  • Philip Lewis 0

    Load the ointernet favucon

  • silkfire 1

    I seem to fail to understand the difference:

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

    and

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

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

    • John King 0

      +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 }
      • Fred SilberbergMicrosoft employee 1

        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 0

          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.

      • Jeff Johnson 0

        +100 to this

      • D Kh 0

        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 property “Name” ,which is required ,to an empty string, or a string with a space. Is the contract now satisfied?

        What does it mean “help developer to set required fields” – so let me set all doubles to 0d and all ints to 0 – how does that help business logic if these were the dimensions of a parcel or something else which is really needed for the structure to make any sense?

        In C# there is NO WAY to guarantee that a structure in memory (e.g. a class instance or a struct etc..) is valid from a business standpoint using compiler ruling alone. You need to execute code, which CHECKS the data against schema of some sort (such as attributes).
        These mechanisms only enforce that you “assign it” in code, but this has very little value, because these assignments are meaningless from the data validation standpoint.

        • Marco Encarnacao 0

          +1

        • Tyler 0

          Very well said. Thank you.

        • Marco Medrano 0

          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.

    • Fred SilberbergMicrosoft employee 0

      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 FirstName or LastName have been given reasonable defaults by the original type author, as the user could have written this and it would look exactly the same:

      public class Person
      {
          public string FirstName { get; init; } = "Default";
          public string LastName { get; init; } = "Person";
      }
      
      • Thomas Eyde 0

        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 out of scope? That could either be part of ‘enable nullable’ or another optional setting. This is a compile time warning/error after all, and will not break anything.

        The init-property says it all, it can be set when initializing which hasn’t happened yet. It’s not an error to not set the property until after initialization. When it comes to set-properties, it’s not an error before first use.

      • Marco Medrano 0

        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.

  • Paulo Pinto 1

    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 0

      Exactly.

  • Stuart Ballard 0

    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 like it would be helpful to actively mention when features that have been announced and previewed get cut – otherwise it’s a nasty surprise for people who’ve been paying attention when suddenly the release happens and it doesn’t contain something they’d had reason to expect.

    • Bill WagnerMicrosoft employee 0

      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 guarantee (see !!), but once it’s in a public preview, we’ll make more announcements if a feature gets removed.

      • Stuart Ballard 0

        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 one that just says something to the effect of “one of the features we’d planned for this release, semi-auto properties, isn’t ready yet and will have to wait for the next release”…

  • Jeff Johnson 0

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

    • D Kh 0

      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 above, C# unfortunately now starts to compete with C++ insanity, for absolutely NO PRACTICAL reason other than “more new features”.

Feedback usabilla icon