C# records are data-focused types with compiler-generated value equality, readable string output, and support for creating modified copies. Apply the record modifier to a class or struct when the stored values define the meaning of an instance.
A record or record class is a reference type. A record struct is a value type. The record modifier adds data-oriented behavior; it does not make every record deeply immutable.
Use a record when two instances containing the same values should be equal. Use a regular class when identity, mutable lifecycle, or behavior is more important than stored values.
Declare a Positional Record Class
Positional parameters generate a primary constructor, public properties, and a Deconstruct() method. A positional record class generates init-only properties.
Example:
// Generate FirstName and LastName properties
public record Customer(string FirstName, string LastName);
Customer asha = new("Asha", "Sharma");
Console.WriteLine(asha);
// Customer { FirstName = Asha, LastName = Sharma }
Use Value Equality
Regular classes use reference equality by default. Records compare their runtime type and stored members, so separate instances with equal values compare as equal.
// These are different objects with the same record values
Customer first = new("Asha", "Sharma");
Customer second = new("Asha", "Sharma");
Console.WriteLine(first == second); // True
Console.WriteLine(ReferenceEquals(first, second)); // False
The compiler synthesizes typed equality, Equals(), GetHashCode(), and the equality operators. If you customize equality, preserve the rule that equal objects must return equal hash codes.
Create Modified Copies with with
A with expression copies a record and applies an object initializer to the copy. It does not change the original instance.
Customer original = new("Oliver", "Brown");
// Create a copy with one changed value
Customer renamed = original with { LastName = "Wilson" };
Console.WriteLine(original.LastName); // Brown
Console.WriteLine(renamed.LastName); // Wilson
For record classes, copying uses the record copy-constructor mechanism. Record structs use value-copy semantics.
Use Nominal Record Syntax
You can declare properties explicitly when you need validation, accessibility, required members, or XML documentation instead of positional syntax.
public record Product
{
// Callers must initialize these properties
public required string Sku { get; init; }
public required string Name { get; init; }
public decimal Price { get; init; }
}
Product item = new()
{
Sku = "BK-1042",
Name = "Database Guide",
Price = 799.00m
};
Record Class and Record Struct
| Type | Semantics | Positional Property Default |
|---|---|---|
| record class | Reference type | Init-only |
| record | Reference type | Init-only |
| record struct | Value type | Read-write |
| readonly record struct | Readonly value type | Init-only |
Choose a record class when you need inheritance or the data is expensive to copy. Choose a record struct for small, self-contained values where value-type copying is appropriate.
// Model a small unit value as a readonly value type
public readonly record struct Temperature(double Celsius)
{
public double Fahrenheit => Celsius * 9.0 / 5.0 + 32.0;
}
Understand Shallow Immutability
Init-only properties prevent replacing a reference after initialization, but they do not freeze the referenced object. Arrays, lists, and mutable objects stored inside a record can still change.
public record Contact(string Name, string[] Phones);
string[] numbers = ["555-0101"];
Contact contact = new("Mia", numbers);
// Legal: mutate the referenced array
contact.Phones[0] = "555-0199";
// The record property still points to the same array
Console.WriteLine(contact.Phones[0]);
Use immutable collections or defensive copies if the complete value must remain stable, especially when records are dictionary keys.
Deconstruct Positional Records
Customer customer = new("James", "Taylor");
// Use the compiler-generated Deconstruct method
var (firstName, lastName) = customer;
Console.WriteLine($"{firstName} {lastName}");
Use Record Inheritance
Record classes can inherit from other record classes. A record cannot inherit from an ordinary class, and an ordinary class cannot inherit from a record. Equality includes the runtime type, so base and derived instances are not equal merely because base properties match.
public abstract record Payment(decimal Amount);
public record CardPayment(
decimal Amount,
string LastFour
) : Payment(Amount);
public record CashPayment(decimal Amount) : Payment(Amount);
Record structs cannot inherit from another class or struct, although they can implement interfaces.
Computed Properties and with Expressions
Compute derived values when accessed instead of storing a value calculated only during initialization. Otherwise a with copy can change its source property while retaining a stale computed property.
public record Rectangle(double Width, double Height)
{
// Always calculate from the current copied values
public double Area => Width * Height;
}
Rectangle small = new(4, 3);
Rectangle wide = small with { Width = 8 };
Console.WriteLine(wide.Area); // 24
Records and Entity Models
Records are usually unsuitable for Entity Framework Core entity types because change tracking relies on reference identity. Records fit value objects, messages, results, configuration snapshots, and data-transfer models more naturally.
Common Mistakes
- Assuming init-only properties provide deep immutability.
- Using a mutable record as a hash-based collection key.
- Choosing a record for an identity-based entity.
- Storing derived values that become stale after a with expression.
- Expecting record structs to support inheritance.
- Overriding equality without a matching hash-code implementation.
Conclusion
C# records remove boilerplate from data-focused models by providing value equality, formatted output, deconstruction, and modified-copy syntax. Choose record class or record struct according to normal reference-versus-value semantics, protect nested mutable data when necessary, and reserve records for values whose contents define equality.