- C# - Home
- C# - Overview
- C# - Environment
- C# - Program Structure
- C# - Basic Syntax
- C# - Data Types
- C# - Type Conversion
- C# - Variables
- C# - Constants
- C# - Operators
- C# - Arithmetic Operators
- C# - Assignment Operators
- C# - Relational Operators
- C# - Logical Operators
- C# - Bitwise Operators
- C# - Miscellaneous Operators
- C# - Operators Precedence
- C# Conditional Statements
- C# - Decision Making
- C# - If
- C# - If Else
- C# - Nested If
- C# - Switch
- C# - Nested Switch
- C# - Switch Expressions
- C# Control Statements
- C# - Loops
- C# - For Loop
- C# - While Loop
- C# - Do While Loop
- C# - Nested Loops
- C# - Break
- C# - Continue
- C# - Foreach Loop
- C# - Goto Statement
- C# OOP & Data Handling
- C# - Encapsulation
- C# - Methods
- C# - Nullables
- C# - Arrays
- C# - Strings
- C# - Structure
- C# - Enums
- C# - Classes
- C# - Inheritance
- C# - Polymorphism
- C# - Operator Overloading
- C# - Interfaces
- C# - Namespaces
- C# - Preprocessor Directives
- C# - Regular Expressions
- C# - Custom Exceptions
- C# - Exception Handling
- C# - File I/O
- C# Advanced Tutorial
- C# - Attributes
- C# - Reflection
- C# - Properties
- C# - Indexers
- C# - Delegates
- C# - Events
- C# - Collections
- C# - Generics
- C# - LINQ
- C# - IEnumerable vs IEnumerator
- C# - Anonymous Methods
- C# - Unsafe Codes
- C# - Tasks and Parallel Programming
- C# - Multithreading
- C# - Extension Methods
- C# - Lambda Expressions
- C# - Async and Await
- C# Modern Features
- C# - Tuples
- C# - Records
- C# - Pattern Matching Enhancements
- C# - Top-level Statements
- C# - Nullable Reference Types
- C# - What's New in C# 11 / 12 / 13
- C# - Global Usings
- C# - File-Scoped Namespaces
- C# Practical & Advanced Usage
- C# - JSON & XML Handling
- C# - Data Serialization & Deserialization
- C# - REST API Calls with Httpclient
- C# - Dependency Injection
- C# - Unit Testing with NUnit, xUnit & MSTest
- C# - Package Management with NuGet
Records in C#
Records in C# are reference types introduced in C# 9.0 that are used to store immutable data. Immutable data means that once a record is created, its values cannot be modified. It is similar to a class but is mainly used for holding and comparing data instead of defining behavior.
Records automatically provide features like value-based equality, hash code generation, and a readable string representation which make them easier to use for data storage and transfer.
Syntax for Records
A record is defined using the record keyword, followed by the record name and its parameters inside parentheses. This is called a positional record −
record RecordName(Type Property1, Type Property2, ...);
A record can also be defined like a regular class with a body −
public record RecordName
{
public Type Property1 { get; init; }
public Type Property2 { get; init; }
}
In this syntax, record is the keyword for defining a record, RecordName is the name of the record, and Property1, Property2 are its properties. The init keyword makes the properties immutable after object creation.
Creating Record Objects
We can create objects of a record using either regular property initialization or positional parameters −
// Using regular property initialization
var person1 = new Person { Name = "Ritu", Age = 25 };
// Using positional parameters
person1 = new Person("Ritu", 28);
Once created a record's properties cannot be changed because records support init-only properties meaning we can assign values when creating the object but not change them later.
// This will give an error person1.Name = "Anita"; // Not allowed
Example
Here, we create a simple record to hold information about a person. For that we create a Person record with two properties, Name and Age.
Note that when we print person1, it automatically gives a readable string representation because records generate a default ToString() method.
using System;
public record Person
{
public string Name { get; init; }
public int year { get; init; }
}
class Program
{
static void Main()
{
var person1 = new Person { Name = "George", year = 25 };
Console.WriteLine(person1);
}
}
Following is the output of the above program which shows the record data in a readable format.
Person { Name = George, Age = 25 }
Positional Records in Detail
Positional records are a shorter way to define records. Using positional syntax, C# automatically generates properties and a constructor. The ToString() method is also created automatically, so we can print the record in a readable format.
Example
In this example, we create a positional record Person with two properties and create the object for Person record, Then we defined the constructor to set the values and then we print the values.
using System;
public record Person(string Name, int Age);
class Program
{
static void Main()
{
var person = new Person("Ritu", 25);
Console.WriteLine(person);
}
}
Following is the output of the program which shows the record with its property values.
Person { Name = Ritu, Age = 25 }
Value-Based Equality
Records are different from classes because they are compared by values instead of references. This means two records with the same property values are considered equal, even if they are two different objects in memory.
Example
In this example, we create two record objects, person1 and person2 with the same property values. When we compare them using == or .Equals(), both return true because records use value-based equality.
using System;
public record Person
{
public string Name { get; init; }
public int Age { get; init; }
}
class Program
{
static void Main()
{
var person1 = new Person { Name = "Aarav", Age = 25 };
var person2 = new Person { Name = "Aarav", Age = 25 };
Console.WriteLine(person1 == person2); // True
Console.WriteLine(person1.Equals(person2)); // True
}
}
Below is the output of the program which shows that both records are equal by value.
True True
With-Expressions
Records are immutable, so their properties cannot be changed after creation. To update data, we can create a new record object from an existing one using a with-expression.
The with keyword creates a new record based on an existing object. It allows us to modify some properties while keeping the original record unchanged.
Example
In this example, we create a record object person1 with some properties. Then we create another record object person2 using the with keyword. This copies all the properties of person1 and changes the Name to "Riya", keeping the record object person1 unchanged.
using System;
public record Person
{
public string Name { get; init; }
public int Age { get; init; }
}
class Program
{
static void Main()
{
var person1 = new Person { Name = "Aarav", Age = 25 };
// Creating a new record with a modified Name
var person2 = person1 with { Name = "Riya" };
Console.WriteLine(person1); // Person { Name = Aarav, Age = 25 }
Console.WriteLine(person2); // Person { Name = Riya, Age = 25 }
}
}
Following is the output of the program showing the original record and the modified copy.
Person { Name = Aarav, Age = 25 }
Person { Name = Riya, Age = 25 }
Record Inheritance
Records support inheritance and can inherit properties from other records. A derived record includes all properties of the base record and can also define its own. Even with inheritance, records keep immutability and value-based equality.
Example
In this example we create a base record Person and an Employee record that inherits from Person record and adds a Department property. Then we create an Employee object to display all the values, including the inherited ones.
using System;
public record Person(string Name, int Age);
public record Employee(string Name, int Age, string Department) : Person(Name, Age);
class Program
{
static void Main()
{
var employee = new Employee("Aarav", 25, "IT");
Console.WriteLine(employee);
}
}
Following is the output of the program showing the inherited record and its properties.
Employee { Name = Aarav, Age = 25, Department = IT }
Records vs Classes
Classes and records both define objects in C# but they work in different ways. Classes are used for objects that can change after creation and can include behavior. Two class objects are considered different even if their properties are the same.
Records, on the other hand, are used to store immutable data and compare objects by their values. Two record objects with the same property values are considered equal, even if they are different instances in memory.
Example Using Class
Let's first see how a class behaves with equality. Here, we create two objects of the class PersonClass with the same property values. Even though person1 and person2 have the same values, classes use reference equality, so both == and .Equals() return false.
using System;
public class PersonClass
{
public string Name { get; set; }
public int Age { get; set; }
}
class Program
{
static void Main()
{
var person1 = new PersonClass { Name = "Aarav", Age = 25 };
var person2 = new PersonClass { Name = "Aarav", Age = 25 };
Console.WriteLine(person1 == person2); // False
Console.WriteLine(person1.Equals(person2)); // False
}
}
Following is the output of the program showing that the two class objects are not equal.
False False
Example Using Record
Now, let's see how records handle the same data. Here, we create two objects of the record PersonRecord with the same property values. Records use value-based equality, so person1 and person2 are considered equal, and both == and .Equals() return true.
using System;
public record PersonRecord
{
public string Name { get; init; }
public int Age { get; init; }
}
class Program
{
static void Main()
{
var person1 = new PersonRecord { Name = "Aarav", Age = 25 };
var person2 = new PersonRecord { Name = "Aarav", Age = 25 };
Console.WriteLine(person1 == person2); // True
Console.WriteLine(person1.Equals(person2)); // True
}
}
Following is the output of the program showing that the two record objects are equal by value.
True True
Key Differences Between Records and Classes
The following table shows the main differences between records and classes in C#.
| Feature | Record | Class |
|---|---|---|
| Equality | Value-based | Reference-based |
| Immutability | Immutable by default (init) | Mutable (can change properties) |
| ToString() | Auto-generated readable string | Shows type name only |
| Use Case | Data storage, DTOs | Business logic, methods |
| Modification | Use with to create copies | Can modify properties directly |
Record Structs (C# 10 and Later)
In C# 10, we can create record structs, which behave like value types instead of reference types. They are useful for small, simple data objects and automatically provide value-based equality and readable output.
Example
Here's an example of how we can create a record struct. We define a record struct Point with two properties, X and Y. Then, we print the object.
using System;
// We define a record struct Point with two properties, X and Y
public record struct Point(int X, int Y);
class Program
{
static void Main()
{
// Create a record struct object
var p1 = new Point(5, 10);
// Print the object
Console.WriteLine(p1);
}
}
Below is the output of the above program.
Point { X = 5, Y = 10 }
Conclusion
In this chapter, we learned that records in C# store immutable data by default and focus on value-based equality rather than reference equality. In updated versions of C#, we can also use features like deconstructors, inheritance, record structs and more.