-
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 "Records" (VS 16.8, .NET 5) #39
Comments
See #77 regarding |
As records are considering positional pattern matching, which is actually a tuple feature, and tuples can have named members, which is actually a record feature, I think there is some overlapping between the two. How about making making seamless translations between struct records and tuples based on position, if types match? Names of the members will be ignored in these translations. Struct records will then just become named tuples I guess. Implementations are already similar. |
@gulshan Tuples are good for places where you might have used a record, but the use is not part of an API and is nothing more than aggregation of a few values. But beyond that there are significant differences between records and tuples. Record member names are preserved at runtime; tuple member names are not. Records are nominally typed, and tuples are structurally typed. Tuples cannot have additional members added (methods, properties, operators, etc), and its elements are mutable fields, while record elements are properties (readonly by default). Records can be value types or reference types. |
Copying my comment on Record from roslyn- Since almost a year has passed, I want to voice my support for the point mentioned by @MgSam -
I think the primary constructor should just generate a POCO. class Point
{
public int X{ get; set; }
public int Y{ get; set; }
public Point(int X, int Y)
{
this.X = X;
this.Y = Y;
}
} And a separate keyword like |
From this recent video by Bertrand Le Roy, it seems records are being defined with a separate keyword and the primary constructor is back with shorter syntax. So far I have understood, the new primary constructor means parameters of primary constructor are also fields of the class- class Point(int x, int y)
// is same as
class Point
{
int x { get; }
int y { get; }
public Point(int x, int y)
{
this.x = x;
this.y = y;
}
} It seems the field access modifier is defult/private and to expose them separate public properties are needed like this- class Point(int x, int y)
{
public int X => x;
public int Y => y;
} I like the idea and I think there should be more discussions about these ideas here. |
We are hoping to have records defined without a separate keyword. Parameters of the primary constructor become public readonly properties of the class by default. See https://github.com/dotnet/csharplang/blob/master/proposals/records.md#record-struct-example for an example. |
Object(and collection, index) initializers getting constructor level privilege for initializing fields/properties can enable some interesting scenarios of complex hierarchical object initialization. |
@gulshan Can you please back up that assertion with an example? I don't see how using an object initializer instead of a constructor enables anything. |
I see a little problem with the proposed |
@miniBill Yes. |
While I'm very much looking forward to the introduction of Records into the language, I really don't like the chosen syntax:
God I miss those... I am so sick of writing dumb constructors! :-P Isn't
A better syntax? It leaves primary constructors open but is still about as syntactically short as is possible. |
@Richiban What about https://github.com/dotnet/csharplang/blob/master/proposals/records.md#primary-constructor-body ? That looks like a primary constructor to me. |
Wouldn't it be better if primary constructors were not exclusive to records? Now, according to this proposal, primary constructors cannot be considered without adding the baggage of extra "record" tidbits. Refactoring a current class to use primary constructors(and thus record) is not a good choice then, as the behavior may change. |
@orthoxerox I guess so, although the spec doesn't mention record parameters having accessibility modifiers, so I can't write:
And anyway, it would be a bit of an abuse of the records feature to accomplish this. My type isn't a record at all, and I don't want structural equality. I remember when the C# team originally dropped primary constructors they said "We don't need this anymore because we're going to have records!" but I don't understand what this has to do with records... Sure, a record is also a type with a primary constructor, but any type should be able to have a primary constructor, not just records. For reference, here are other languages which all support both primary constructors and records: F#:
Scala:
Kotlin:
Meanwhile, in C#, we're still writing this:
|
@Richiban public class Greeter(string Name)
{
private string Name { get; } = Name;
public void Greet()
{
Console.WriteLine($"Hello, {Name}");
}
} |
Why do you need the p = p with { X = 5 }; Wouldn't it be equally understandable when there were a var r = p.{ X = 5, Y = 6 }.ToRadialCoordinates(); |
@gafter If we go with the record definition |
@Richiban No, if a property is explicitly written into the body by the programmer, as in my example, then the compiler does not produce one. That is described in the specification. |
By the way, do I understand the spec right that class records must be either sealed or abstract? There are serious problems with allowing classes to derive types that have defined custom equality: https://richiban.uk/2016/10/26/why-records-must-be-sealed/ |
If records remain the only way to get auto-generation of Equals and HashCode then I think they absolutely should not be sealed. As you yourself state in your post, doing a simple type check in the equals method solves the issue you bring up. Seems pretty Byzantine to wall off an entire use case because of the fact that developers "might" misuse a feature. Getting structural equality right in C# is already a minefield that most sensible developers let IDE tools generate code for. Compiler autogeneration of the equality methods should be enabled for the widest net of situations possible. |
First, that might not be what is wanted if you want are reference type. Second, that approach does not work for helping with:
So you get barely any help here at all. |
This is not true. Records define |
I don't really know waht that is. But you can certainly feel free to start a discussion on the topic. If we like it, we could move forward with it in the language. |
Yes. Records met that bar.
That's exactly not the case. Records are very domain-general. I.e. we expect to see records in effectively all domains. That's one of the reasons they warranted this treatment.
I have no idea where this position is coming from. Why on earth would records be disallowed anywhere? If you're already wriitng out the 40 line version, why would someone stop you from using the 1 line equivalent? :) |
I disagree, and I think many others will, that Why would This is not to say records will be found to non-useful by many; just that in the spectrum of features being added to C# at the keyword level, this is the weakest and most problematic one to date, and one that does not have a "pit of success" under it. Again, time will probably tell. |
Data processing is literally part of practically every domain :)
There is nothing that ties this to functional programming. Working with data and values comes up everywhere.
Yes... that's what 'records' are. The feature that lets you do this. As i mentioned already, trying to do this today with all the features we've shipped so far means 40+ lines for this very simple, domain-general, concept. We didn't like that, so we made a feature for it. And now it can be a single line that all devs can use for practically any problem space.
I really don't get how you can say that. I've already outlined the problems people had prior to this. #39 (comment) shows all the hoops people need to jump through today. Part of those hoops are practically very hard to get right. This is especially true if you dn't have something like System.HashCode. It's also super difficult for people to get value-equality correct, among other things. On top of all of this, the maintenance cost of those problems is quite high. The state prior to records is literally the opposite of a 'pit of success'. You have to put in a ton of effort, and it's a great pain to ensure correctness and keep maintained. Literally all of that goes away with 'records'. You can now write a single line, that just states the data you care about, and all of this is taken care of for you. It is definitionally a 'pit of success' because the core design around them was generating the appropriate defaults the ecosystem had already settled on for value types, instead of forcing the user to do that and maintain it.
You haven't stated any weaknesses or problems with it. :-/ |
I would like to ask, will it be possible to have an immutable list of strings in a record? How about an immutable set of strings? Will it generate correct equals and hash code methods? |
Records always generate the same equality and hash code methods. They defer how equality is determined to the types of the fields in the record. So if you're using an immutable list or set of Strings it's up to that collection class to evaluate equality. |
I see. That sounds a bit impractical, because even var a = ImmutableList.Create(42);
var b = ImmutableList.Create(42);
// This prints False
Console.WriteLine(a.Equals(b)); So, I would have to create a wrapper of a list / set that implements equals and hash code using its elements, and then use this wrapper in a record as a field, am I right? |
Correct, you'd either have to provide wrappers and override the Equals/GetHashCode implementation of the element types, or you need to override the Equals/GetHashCode implementation of the record itself. |
This sounds interesting. Is there a way to combine overridding with the generated version? For example, if my record has 5 string fields and 1 list of strings, I'd like to write something like public override bool Equals(MyType obj) => base.Equals(obj) && obj.List.SequenceEquals(List) (Perhaps it would require some attribute on the string List indicating that it should not be including in the generated version?) |
No, it's an all-or-none proposition. If you override the The team had considered some approaches to enabling some flexibility around equality but were considered that it would explode into a whole series of possible features. For these scenarios they instead recommend using source generators which could inspect attributes on the members of the record and emit custom equality members. |
See dotnet/roslyn#49469 as to a very real reason why this feature (at least in part) may not be recommended or even disallowed in places. It's easier to recommend avoiding the feature than for many people to understand when, how and where it can be consumed. Use it internally, sure. As part of an API surface... sort of... and that is how you can end up with the recommendation/disallowing. |
This is a legal statement from a struct (or class) construction today: But this statement from a record construction is not: Is there a rational for this difference? Will there be opportunity to post-process a record? To instead support: Will there be opportunity to pre-process a record? To support: |
@fubar-coder No, I am looking for information of current plans for the record features. There have been discussions about overriding cloning, factories, validation, etc and I am not sure if the mentioned situation is within the scope of that. |
Does anyone know the plans for custom factories and cloning overrides (which I think is the way to address the above)? At some point I am pretty sure I have read about it in some spec but it does not seem to have made it, yet? |
@MadsTorgersen , @333fred how many of the tasks have been completed? |
All of them. This shipped in C# 9. I updated the checkboxes. |
Should this issue be closed? |
This issue will be closed when it has been specified in the ECMA spec. This is what the |
LDM notes:
...
The text was updated successfully, but these errors were encountered: