-
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
Records: Metadata determination of whether a type is a record #4121
Comments
This is not the conclusion of LDM when we discussed this previously. |
The previous conclusion of the LDM was that it was possible we'd need to keep this method around, regardless of whether we end up implementing generalized with by allowing any type to have this method. That has nothing to do with whether or not using this method will always be a good way to determine if a type is a record, and is what we need to discuss on Wednesday. |
Alright, talked with Jared offline. We were talking past each other about part of my wording above: regardless of whether we implement generalized with support via As to implementation strategy, we can take a page out of how we do extension methods: in 16.9, we emit an attribute on both the module and on the record itself. If the attribute exists on the module, then we only consider the attribute when determining whether the type is a record. If the attribute does not exist on the module, we only consider the presence of a |
FWIW, just to add some context: an issue recently came up in Bogus to support C# 9 record types here: bchavez/Bogus#334. Currently, given a positional record of type record Dog(string Name);
var dog = new Faker<Dog>()
.RuleFor(d => d.Name, f => f.Name.FindName())
.Generate();
To get a positional record type working with record Dog(string Name);
var dog = new Faker<Dog>()
.CustomInstantiator( f => new Dog( f.Name.FindName() ) )
.Generate(); To ease some of the developer pain and help ergonomics, I was looking to reflect over It's a hack for sure and probably not the greatest, but seems like could work. |
@bchavez Wouldn't it be better to treat records the same as ordinary classes, since that's what they output? How do you handle classes that have no parameterless constructor? |
There should also be some way to tell which constructor is the primary constructor. |
ATM I detect records like this:
Another way to detect records can be to check if If one can determine that a type is a) a record and b) has compiler generated |
Why is it important to distinguish them as records? It's more important to recognize each type for the functionality they offer. Non-records may definitely offer value equality, and records are capable of not having value equality. IMO it's like iterators. They're an implementation detail, an aid to the developer. All you know is that you have an |
Out of curiosity, why? What would make a record with a primary constructor different from a regular class with a single non-parameterless constructor? Or a record with both a primary constructor and a secondary constructor different from a regular class that happens to have two constructors? |
@HaloFour again the motivation is asserting a type has value semantics, then you can ie use it as a dictionary key and composing it in other value types .. anyway that ship has sailed but at least there should be a way to tell if a type is a record with no user defined equals/gethashcode as a second best |
IMO |
There is nothing about being a record that guarantees that. Records are only default readonly, but anyone can define a set operator on any record property. |
@kofifus asked
The syntactic sugar is far from trivial. There are a bunch of extremely common mistakes that records eliminate, preventing whole classes of subtle bugs. These include (not an exhaustive list)
|
thanks @HaloFour that was useful |
I think the documentation about this should be clearer. |
To add another use case here: for Microsoft Fakes we generate Stub classes for all types in an assembly, and it looks like so:
The addition of records made this cause failures because we did not account for checking if
|
It is correct to say this, as long as you include the second part of that sentence: |
You're right. I skimmed over that and didn't register it. Is there planned work to remove the inheritance constraints and/or to consider adding a metadata indicator? |
Not yet. |
We have no plans to do anything more here. Closing out. |
Tracking issue for discussion in LDM. We have several customers (such as EF, F#, the IDE) who have taken implicit dependency on the
<Clone>$
method name as a valid way to determine whether a type is a record or not, and there have been performance issues with this approach (as loading method metadata can be expensive unless special codepaths are added to look for a specific method, see dotnet/roslyn#48935). This is not a forward-compatible way of determining whether a type is a record, so we need to discuss adding some real metadata in the 16.9 timeframe to record generation for future determination of whether a type is a record or not. Some points to discuss:/cc @jaredpar
LDM Discussions:
https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-11-11.md#isrecord-in-metadata
The text was updated successfully, but these errors were encountered: