C# 11 preview: generic math, required members, and more
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:
- Static abstract and static virtual members in interfaces
- Relaxed right-shift requirements
- Unsigned right shift operator
- Numeric
IntPtr
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
What about adding record-like syntax for regular classes? Something like this:
I think it would be very useful for Dependency Injection, as it is quite common to write constructors just for initializing fields.
I’d perfer the Typescript one
IIRC the “primary constructor” proposal would make it like this:
but i think that proposal is older than records so who knows. 🤔
There is already a design under consideration from the team regarding this: https://github.com/dotnet/csharplang/blob/main/proposals/non-record-primary-constructors.md
It is called C# primary constructor – it is coming.
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.
+1
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:
“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:
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.
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.
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
, if the referenced assembly is actually a ref assembly, and not a real assembly.
“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.
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.Is it possible to use or at least consume required feature in VB?
Promises
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.
+1
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.
EWWW!! Stop bloating c# already. It was a great language and now you’re adding all this bloat, it’s gross!
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).
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.
+1
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.
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
orstackalloc
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.
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.
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
-1
-1
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.Agreed 10000000000%
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?
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.
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.
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.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.
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.
Can you tell me what the following code means?
Why not ?
The first snippet just failed to compile. If a property is marked as
required
, it must be settable or initializable. Also,set
andinit
cannot coexist on the same property so putting required on the property instead of accessor sounds fine to me.Fields can also be marked
required
, so for consistency we wanted therequired
keyword as a modifier on the member itself.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.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.
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.
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.
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.
Better yet, since required implies init:
Load the ointernet favucon
I seem to fail to understand the difference:
and
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?+1
I think we don’t need
required
keyword theinit
+reference nullable
should means “required” , what we need is actullyDuring the design of the feature, we commonly saw people conflate nullability and requiredness. Unfortunately, this is a mistake: properties can be
struct
s , for example, or you might want to require a nullable property (ie, thatnull
is a valid value to set, but you want your consumer to make that decision intentionally).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.
+100 to this
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.
Mark Seemann has an interesting blog post about just that: https://blog.ploeh.dk/2022/08/22/can-types-replace-validation/
The short answer is: No you can’t.
+1
Very well said. Thank you.
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.
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
orLastName
have been given reasonable defaults by the original type author, as the user could have written this and it would look exactly the same: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.
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.
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.
Exactly.
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.
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.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”…
Could we just replace init with required? To me, required implies init…
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”.