- "You don't see the gazillion tabs I have in my other window... It's actually mostly stackoverflow posts on how to use System.IO.Pipelines."
We have 3 options on this issue, centered around how whether we want to make a change and, if so, how far do we want to take it.
- Change nothing. The scenario remains an error.
- Allow unconditional casts.
- Allow
as
casts as well.
We feel that this case is pretty pathological, and we have trouble coming up with real-world examples of APIs that would need to
both hide a public member from a base type and initialize it in the constructor to some value. It would also be odd to allow it
in the constructor while not having a form of initializing the property from an object initializer, which is the new thing that
init
enables over set
methods. If we continue seeing a need to name hidden members, perhaps we can come up with a feature that
generally allows that, as opposed to solving one particular case in constructors.
We'll go with 1. The proposal is rejected.
We're revisiting the decision made the last time we talked about record structs. In that meeting, we decided to disallow record
structs from defining customized with
semantics, due to concerns over how such structs would behave in generic contexts when
constrained to where T : struct
. If we do disallow this customization, do we need to disallow methods named Clone as well?
And should we also disallow copy constructors? In looking at the questions here, we spitballed some potential ways that we could
allow customized copies and still allow record structs to be used in generics constrained to where T : struct
, potentially by
introducing a new interface in the BCL. A with
on a struct type param would check for an implementation of that interface and
call that, rather than blindly emitting a dup
instruction as our original intention was. We think it's an interesting idea and
want to pull it into a complete proposal, so we're holding off on making any decisions about allowed and disallowed members in
record structs related to copying for now.
On hold.
A question was raised during initial review of the specification for record structs on whether we need to keep PrintMembers
.
Struct types don't have inheritance, so we could theoretically simplify that to just ToString()
for this case. However, we
think that there is value in minimizing the differences between record structs and record classes, so conversion between them
is as painless as possible. Since users can provide their own PrintMembers
method with their own semantics, removing it
potentially introduces friction in making such a change.
Keep PrintMembers
. The behavior will be the same as in record classes.
During review, a question was raised about our original decision here with respect to generating the actual equality implementation
for record structs. Originally, we had decided to not generate a new Equals(object)
method, and have making a struct a record
be solely about adding new surface area for the same functionality. Instead, we'd work with the runtime to make the equality
generation better for all struct types. While we still want to pursue this angle as well, after discussion we decided that this
would be another friction point between record classes and record structs, and could potentially have negative consequences if
we don't have time to ship better runtime equality for structs in .NET 6, as many scenarios would then just need to turn around
and implement equality themselves. In the future, if we do get the better runtime-generated equality, that could be added as a
feature flag to System.Runtime.CompilerServices.RuntimeFeature
, and we can inform the generation of equality based on the
presence of the flag.
We will generate equality methods in the same manner as proposed in the record struct specification proposal.
The question here is whether we can allow field initializers in structs that have a primary constructor with more than zero
parameters. The immediate followup to that question, of course, is can we just finally allow parameterless constructors for
structs in general, and then field initializers just work for all of them? We're still interested in doing this: the
Activator.CreateInstance
bug was in a version of the framework that is long out of support at this point, and we have universal
agreement behind the idea. The last time we talked about the feature we took a look at non-defaultable value types in general,
and while there are interesting ideas in there, we don't think we need to block parameterless constructors on it.
Let's dig up the proposal from when we did parameterless struct constructors last and get it done, then this question becomes moot.
The record class specification, and the record struct specification, states:
The synthesized override of
GetHashCode()
returns anint
result of a deterministic function combining the values ofSystem.Collections.Generic.EqualityComparer<TN>.Default.GetHashCode(fieldN)
for each instance fieldfieldN
withTN
being the type offieldN
.
We're not precise on the semantics of "a deterministic function combining the values" here, and the question is whether we should be more precise about the semantics of that. After discussion, we believe we're fine with the wording. It does not promise determinism across boundaries such as different executions of the same program or running the same program on different versions of the runtime, which are not guarantees we want to make.
Fine as is.