-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Champion "Allow no-arg constructor and field initializers in struct declarations" #99
Comments
@jnm2 I don't think that it's about the benefit but more of a consistency so yes symmetry but dunno what it means for the run-time, I'm not sure what would happen when you define a parameterless constructor and set the fields, I voted because I hope that we could have default values but dunno. |
Downvoting this as per my comment for Roslyn #13921. For a struct, |
I agree. If we're going to get non-nullable reference types and structs with parameterless constructors, then we should be able to override |
I really hope that they will design and implement this properly. |
To play Devil's advocate, why is it expected that Does C# support consuming structs with default constructors today? You could always define one using IL or some other language. How about the IL that C# produces for a generic method with a The concept of a "default" for struct types is interesting but even in the case of a blittable default you're talking about adding some serious overhead. |
@HaloFour Well, it might be possible to mitigate the overhead by an attribute? like opt-in/out? |
Your devil's advocate is a good question. To my mind, the answer is "and that is exactly what's wrong with reference types". It is the driver behind non-nullable reference types. SO just at the time when we are looking to address the "null problem", we'd be introducing the same problem in to structs. |
I think we're wading into the territory of a different discussion here. I just tested and answered my questions. C# does currently support consuming structs that define default constructors. It just doesn't support defining them. There is one notable exception and that's in a generic method where the type has a constructor constraint. I believe that this was already considered a bug in the JIT. |
We would need the core clr bug https://github.com/dotnet/coreclr/issues/6843 fixed before we could do this. |
What down-to-earth benefits would we gain from this proposal? I'm inclined to say this raises the complexity of the mental model with no benefits in sight, since no one in Codeplex, /roslyn or this thread has mentioned a single pragmatic benefit despite people repeatedly asking. |
@jnm2 What do you mean? are you saying that it has zero benefit? Just as a real world example, I have a text parser where the scanner returns a struct that looks like this:
Now, when you create a new instance of the struct or use default Update: Rephrased. |
Let's say I have a type that holds information on a car gearbox, including the number of gear stick positions and the gearing ratios and there will always be at least two positions (forward and reverse). Currently, my options are to use a class (so I can control its construction) and suffer null ref problems, or have a struct. The latter then either suffers the equivalent of the null ref problem if created with If this feature were correctly implemented, eg via an overridable As the proposal currently stands though, |
BTW, some people workaround this by doing something like the following:
Now, this can work but doing this for every field is just boilerplate. |
@DavidArno default is not a call. Default means zero-fill. Default happens when you create an array. Default happens to all class fields before your initialization code runs. Default happens due to a number of things, and it's for safety so that you don't get junk. Treating it like a call makes no sense because it isn't a call. We need it to be what it is: zero-fill. |
I would expect that That means that developers should be aware that their struct can be created without running any constructor. |
@eyalsk same thing about default. It means "I want this memory to be zero" and changing that semantic would be next to impossible, let alone desirable. I'm not sure I follow you with the rest of what you showed. Those parameter defaults should only apply if you use that constructor. |
I'd like to reiterate that this proposal has nothing to do with changing how the C# compiler interprets Of course since neither C# nor VB.NET permitted defining a struct with a parameterless constructor the odds of running into one would be exceptionally low. So low that a bug made it into the BCL assuming that such a beast wouldn't exist. But it's quite possible for some other language to define such a struct, and C# already respects such structs by invoking their constructors. So, in my opinion we need to move on from the conversation about whether structs should have parameterless constructors or whether C# should consider |
Let's disregard my previous post because it wasn't clear so let's focus on the following examples instead:
Possible today but when you do
Not possible today but I'd like to have it so in the case of the following code:
When I'd do Now, it's a not a deal breaker but I think that having the ability to do it will fix the slight disparity with classes and people won't need to circumvent or fight the language and do something like this:
|
Currently, @JMN2, |
That only affects specific uses with generics. When using a struct directly the C# compiler will always invoke the parameterless constructor if it exists. That is also being regarded as a bug in the CLR which at the time of implementation was itself a breaking change. Even though I do largely use the generic type argument syntax, I am referring to all uses of structs in my argument. If you were handed an assembly the contained a struct called void Test1() {
var s = new FooStruct(); // invokes parameterless constructor
}
void Test2<T>() where T : struct {
var s = new T(); // invokes parameterless constructor
}
void Test3<T>() where T : new() {
var s = new T(); // does not invoke parameterless constructor
} As mentioned in https://github.com/dotnet/coreclr/issues/6843, structs with parameterless constructors are a part of the CLI spec. As far as I can tell they also meet CLS requirements. C# is required to at least respect them. So is the CLR. |
@eyalsk So all you are really asking for is struct field initializers. (Field initializers would be possible if you call I suppose it's a matter of perspective. Since |
@jnm2 I can relate to what @DavidArno says but at the same time I wouldn't mind that I wasn't speaking about
You're right but at the same time it depends: in my case I'm not returning an array but an Beyond that I think that it might be nice to fix the slight disparity between classes and structs that might be a surprise to new comers. |
@eyalsk But because |
Moved this to the working set following recent LDM on record structs (https://github.com/dotnet/csharplang/blob/master/meetings/2021/LDM-2021-01-27.md#field-initializers). |
Breaking: My unit tests revealed that not only In fact,
They meant it for the |
Presumably this is less of a concern now that C# and .NET are shipped together in lockstep and C# 10 would not be supported on .NET versions lower than 6. |
I don't believe this is covered under our support matrix. Effectively, all bets are off with this. If you use language features not explicitly marked as supported for that version, then effectively you get 'undefined behavior'. |
I also think that default(T) and new T() should always remain the same. If I initialize an array of struct values, I expect the default constructor to run, no matter if I defined it or not. I don't have a solution for the performance issues this could create. One thing is for sure, in a closed project, if I define a default constructor and do complex things in it, I'd accept that any array initialization would be slower. Allowing users to define a custom default value might be a better option for performance. |
This is already not the case if you define a parameterless constructor in IL, and has been the case since C# 1.0. See earlier in this thread: #99 (comment)
|
Is there a way to get a warning on use of |
Yes. An analyzer would be able to look for such a thing and report such a problem for you in your code. You could also publish such a thing if you thought it would be valuable for others :) |
I think such an analyzer was (going to be?) added to one of the built-in analyzer libraries, but I'm not sure what the status of it is. I do think we should push the ecosystem away from |
See also dotnet/roslyn#13921 dotnet/roslyn#1029
LDM history:
The text was updated successfully, but these errors were encountered: