DEV Community

Cover image for C# 9 record is not sugar for simple property POCO

C# 9 record is not sugar for simple property POCO

shps951023 profile image Wei ・3 min read

C# 9 record is not sugar for simple property POCO

Today I upgrade project to .NET 5 and try to rewrite package using C#9 syntax. I found that the previous thought for record is just a simple property POCO’s sugar is wrong.

Here, simple property POCO is defined as public class Type {public string ID{get;set}/*..*/} simple class code.

1. rocord is indeed a class after compiler, but it is not a simple property POCO class.
Look at the IL Spy decompilation code and find that the system has done a lot for it.

2. Default property is {get;init;} not {get;set;}, this means setting moment is in constructor, and that represent record by default is immutable and thread-safe, because the same value is achieved.

So when you use the Dapper similar framework to query the POCO data and want to modify the attributes, it will report the CS8852 Unable to modify error.

3. Change of Equals default logic .
You can see the example written by TimCorey , see the == difference between the default class and record, online test link

public class Program
    public static void Main()
        var record1Obj1 = new record1(FirstName: "Lin", LastName: "WeiHan");
        var record1Obj2 = new record1(FirstName: "Lin", LastName: "WeiHan");
        Console.WriteLine(record1Obj1 == record1Obj2);//true

        var class1Obj1 = new Class1() { FirstName = "Lin", LastName = "WeiHan" };
        var class2Obj2 = new Class1() { FirstName = "Lin", LastName = "WeiHan" };
        Console.WriteLine(class1Obj1 == class2Obj2);//false

public record record1(string FirstName,string LastName);

public class Class1
    public string FirstName {get;init;}
    public string LastName{get;init;}

Enter fullscreen mode Exit fullscreen mode

Because the record override == with Equals that as long as the same record type, and propertys value are same the system will be identified as true , which is commonly known structural equality , can look IL Spy decompile the code

    public virtual bool Equals(record2? other)
        return (object)other != null && EqualityContract == other!.EqualityContract && EqualityComparer<string>.Default.Equals(FirstName, other!.FirstName) && EqualityComparer<string>.Default.Equals(LastName, other!.LastName);
Enter fullscreen mode Exit fullscreen mode

It is different from the object class default to get RuntimeHelpers.GetHashCode Handle logic.

4. GetHashCode did similar logic, therefore propertys value are same,hashCode'll be same' , test link

IL Spy decompile Code

    public override int GetHashCode()
        return (EqualityComparer<Type>.Default.GetHashCode(EqualityContract) * -1521134295 + EqualityComparer<string>.Default.GetHashCode(FirstName)) * -1521134295 + EqualityComparer<string>.Default.GetHashCode(LastName);
Enter fullscreen mode Exit fullscreen mode

5. Note that you can't treat record as a must immutable, because the reason is like below code that is unlimited...

public record record2
    public string FirstName {get;set;}
    public string LastName{get;set;}
Enter fullscreen mode Exit fullscreen mode

Allow {get;init;} to {get;set;} , will lose immutable and thread-safe behavior.

6. Record will help generate a readable ToString implementation. The
following image shows the difference between the ToString generated by a general class and a record

7. Record helps to generate extend IEquatable<Type> and implement strong typing, public virtual bool Equals(Record1? other)

which means it can avoid the original public override bool Equals(object? obj) need unboxing and boxing performane problems

Read information:

Discussion (2)

Editor guide
mileswatson profile image
Miles Watson

Thanks for posting this! I’ve been trying to understand more about the behind-the-scenes workings of C# / .NET, and this breakdown has really helped with that.

shps951023 profile image
Wei Author

It's my pleasure 😉