-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Test plan for "records" #40726
Comments
Here's a simplified example of where I ran into this while experimenting with using records: (SharpLab) public record TranslatedDeclaration
{
public TranslatedDeclaration Original { get; } // Seemingly innocent reference to the original instance
public string Name { get; init; }
internal TranslatedDeclaration()
=> Original = this;
} (The idea here is that external consumers can create variants of Not sure what (if anything) the compiler can really do about this except maybe emit a warning if it detects the potential for a cycle to form. Although such a warning wouldn't be especially actionable beyond removing the potential cycle without some ability to customize the synthesized methods beyond implementing them all manually. (At which point records basically become glorified classes-with- It may just be that records-with-cycles becomes an anti-pattern to be avoided, but I do feel like it's significantly easier to bump into this problem compared to similar ones when you're implementing things manually. |
@PathogenDavid Filed #48646. |
@tmat Thanks! Gave that issue a follow. |
Hmm, I'm not sure if this is a good place to mention this, but positional records don't work with System.Text.Json deserialization because they "miss a parameterless constructor". It seems very important to add functionality to System.Text.Json to be able to deserialize positional records as I reckon that will be a pretty regular use-case. At least it is for me. |
@uxsoft I believe the issue you're talking about is dotnet/runtime#38539 which was fixed in dotnet/runtime#38959 It's working fine for me on RC2: using System;
using System.Text.Json;
Console.WriteLine("Hello World!");
Person david = new("David", "Maas");
Console.WriteLine($"Record: {david}");
string json = JsonSerializer.Serialize(david);
Console.WriteLine($"Serialized: {json}");
Person david2 = JsonSerializer.Deserialize<Person>(json);
Console.WriteLine($"Deserialized: {david2}");
record Person(string FirstName, string LastName); Output:
|
We have individual issues tracking follow-up work. Closing this umbrella issue |
Relates to championed issue: dotnet/csharplang#39
Spec: https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/records.md
Records board: https://github.com/dotnet/roslyn/projects/57
LDM:
ToString
: should we always print a trailing commaR { Property1 = 42, Property2 = 43, }
? (no)ToString
: can we deal with stack overflow potential? (do nothing special for now)ToString
: should we try to quote/escape values, for exampleR { IntProperty = 42, StringProperty = "hello" }
? (no)ToString
: should we omitToString
on abstract records? (no)ToString
: shouldPrintMembers
callToString()
on values to avoid boxing them inbuilder.Append((object)value)
? (yes)ToString
: should we try and avoid the double space in empty recordR { }
? (yes)ToString
: do we want to economize a call toStringBuilder.Append
to print<name> {
? (maybe)EqualityContract
if the record type is sealed and derives fromSystem.Object
? (no)record Derived : Base(0) { }
(yes)bool Equals(R? other)
andType EqualityContract { get; }
andR? <Clone>$()
andvoid Deconstruct(out string? NotNullableStringProperty)
. (issue Records: Incorrect nullable annotations for generated Equals override #47627)object
? (no)with
on non-record types (maybe with user-definedClone()
method)? (out of scope for C# 9)data
properties (out of scope for C# 9)with
-able? Allowst with { ... }
with use of: where T : I, with
(like we could do: where T : I, new
)? (out of scope for C# 9)with
-able? (out of scope for C# 9).ToString()
(yes)params
for now)record Base { public int P => 0; } record Derived(int P);
and how does it work? (maybeDerived
getsnew int P { get; init; }
, or maybe it's an error)data
vs.record
syntax (record Person(...)
for next preview)_ = new C() { readonlyField = null };
be allowed in a method on typeC
? (comment) (punted out of C# 9)IsExternalInit
type (confirmed)init void Init()
(punted out of C# 9, strong interest, issue withAdd
methods in BCL immutable collection types)with
initializer with expressions allowed in other kinds of initializers, such as[0] = 2
or values forIEnumerable/Add
collection. (punted out of C# 9)Record declarations
Compiler:
with
expressions #44877)record
is conditional on LangVersion ("preview" now and "9" later)abstract record M(int i, int j);
(parses differently based on LangVersion)bool record;
(remains a field or local, regardless of LangVersion)Clone
extension method allowed? (no)Clone
withparams
or optional parameters? (no)data class Person(string Name = null, decimal height = 0) { }
)TypeNamedRecord_*
)partial record
reiterate its base without providing arguments Bind base arguments fo records #44876 (comment)sealed override <>Clone
record C(Property int) { public int Property { set { } } }
(no getter in user-defined property)AttributesOnPrimaryConstructorParameters_09_CallerMemberName
)PartialRecord_ParametersInScopeOfBothParts
)Deconstruct
and deconstruct (seeDeconstruct_*
)RecordInsideGenericType
)InterfaceImplementation
)Productivity:
class
,data
andwith
(PR Recommenders fordata
andwith
#43971)record
keywordClone()
method (not applicable since unspeakable)in
but notref
/out
/this
in record parameterswith
expressionswith
expression (get theClone()
method)with
on a ref localGetSymbolInfo
on the properties inside awith
(seeWithExpr28_WithAwait
)with
(seeTestWithExpression
)with
on TRecord (fixed)_ = tRecordAndInterface with { /* try to set interface members */ };
(fixed)with
expression ondynamic
type (disallow, seeWithExpr24_Dynamic
)Obsolete
on record type carry over to synthesized methods? (no)WithExpr27_InExceptionFilter
)await
inside awith
(seeWithExpr28_WithAwait
)new Person() with { First = null, First = null }
(WithExpr_NullableAnalysis_*)with
initializers with kind =ObjectInitializerExpression
instead ofWithInitializerExpression
? (done)OperationCloner
and CFG should supportwith
expressions (addedIWithOperation
)with
in expression trees (TestInExpressionTree
)with
expression (PR)init only
Speclet: https://github.com/dotnet/csharplang/blob/master/proposals/init.md
IsExternalInit
(API request, PR)Productivity:
init
keyword #43865)init
keyword #43865)record
(exists)API:
IsInitOnly
onstatic int Property { init { } }
IsInitOnly
) and feature in specdata
properties (out of scope for C# 9)sealed
modifier (LDM 7/6/2020)The text was updated successfully, but these errors were encountered: