C#
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C#
-
Section 4: Working with Data
-
Section 5: Error Handling
-
Section 6: Delegates and Events
-
Section 7: Async Programming
-
Section 8: More Language Features
-
Section 9: File I/O and Serialization
-
Section 10: Networking in .NET
-
Section 11: The .NET Ecosystem
-
Section 12: Memory and Performance
-
Section 13: Concurrency Beyond Async
-
Section 14: Reflection and Attributes
-
Section 15: Testing and Best Practices
-
Section 16: Design Patterns in C#
-
Section 17: Standard Library Deep Dive
-
Section 18: Data Structures and Algorithms in C#
-
Section 19: GUI and Desktop Development Overview
-
Section 20: Practical Projects
-
Section 21: More Practice Exercises
-
Section 22: More Standard Library and Text Processing
-
Section 23: More Design Patterns
-
Section 24: More Projects
-
Section 25: Interview Practice
-
Section 26: C# Keywords Reference (Modifiers)
-
Section 27: C# Keywords Reference (Statements)
-
Section 28: C# Keywords Reference (Operators)
-
Section 29: BCL: System.Collections.Generic
-
Section 30: BCL: System.Linq
-
Section 31: BCL: System.Threading
-
Section 32: BCL: System.IO
-
Section 33: BCL: System.Text and System.Text.Json
-
Section 34: BCL: System.Net.Http
-
Section 35: C# Language Specification Topics
-
Section 36: More Practice Exercises
-
Section 37: More Async Patterns
-
Section 38: More BCL: System.Reflection and System.Diagnostics
-
Section 39: Nullable Reference Types In Depth
-
Section 40: C# Records and Pattern Matching In Depth
-
Section 41: Dependency Injection Deep Dive
-
Section 42: More Interview and Whiteboard Practice
234: Positional Records vs Property-Based Records
I once worked with a developer who was absolutely enamored with the brevity of C# records. He converted our entire Domain Transfer Object layer to positional records because, as he put it, "why write ten lines when you can write one?" It was great for a week. Then, the business requirements changed, and we needed to add an optional DiscountCode to our Order record. Because he used the positional syntax, that one addition broke every single line of code where an Order was instantiated across the rest of the application. He spent an entire afternoon hunting down constructor calls just to add a single optional field.
The Speed of Positional Records
When you write a record like public record User(string Username, string Email);, you're using what we call a positional record. You aren't just defining a data structure; you're telling the compiler to generate a primary constructor, a set of public init-only properties, and a deconstructor—all in one line. It's incredibly satisfying for small, immutable data packets.
public record Point(double X, double Y);
// Usage is fast and clean
var start = new Point(0, 0);
var (x, y) = start; // Deconstruction works out of the box
I love these for internal state or simple coordinates. However, the "magic" here is that the primary constructor is the only way to create the object by default. If you add a third parameter to that record, every new Point(...) call in your codebase now has a compiler error. This is the "rigidity" that caught my colleague off guard.
Regaining Control with Property-Based Records
If you find yourself needing optional parameters, default values, or specific logic during initialization, you should switch to property-based records. This looks much more like a traditional class, but you keep all the value-based equality and the with expression capabilities that make records special.
public record Order
{
public int OrderId { get; init; }
public string CustomerName { get; init; } = "Guest"; // Default value
public decimal Total { get; init; }
public string? DiscountCode { get; init; } // Optional field
}
// Now I can instantiate it without worrying about constructor order
var myOrder = new Order
{
OrderId = 101,
Total = 59.99m
};
By using the { get; init; } syntax, you're still ensuring immutability—the object cannot be changed after it's created—but you've decoupled the object's creation from a strict constructor signature. I generally lean toward this approach for any record that is likely to evolve over the course of a project. It saves you from the "refactoring nightmare" I mentioned earlier.
Which One Should You Actually Use?
You might be wondering if you can mix them. You can, but it usually gets confusing. The general rule of thumb I follow is based on the "stability" of the data. If you are representing a mathematical concept (like a ComplexNumber or a Vector3) where the properties are fixed and essential, go positional. It's concise and expresses the intent perfectly.
For everything else—API responses, database entities, or configuration objects—use property-based records. The slight increase in verbosity is a small price to pay for the flexibility of object initializers. Remember, the with expression works identically for both: var updatedOrder = myOrder with { Total = 49.99m };. You don't lose any of the "record magic" by choosing the more explicit syntax.
📋 Practical Task
Refactoring the Warehouse Inventory DTO
You have been handed a legacy piece of code where a Product record was defined positionally. The team is complaining because adding a new optional WarehouseLocation field is causing too many breaking changes across the system. Your task is to refactor the Product record from a positional record to a property-based record to allow for optional initialization.
Requirements:
- Convert the
Productrecord frompublic record Product(string Sku, string Name, decimal Price);to a property-based record. - Ensure all properties remain immutable (use
init). - Add an optional property
string? WarehouseLocationthat defaults tonull. - Add a property
int StockQuantitythat defaults to0. - In your
Mainmethod, instantiate aProductusing object initializer syntax, providing only theSkuandPrice, leaving the others to their defaults.
// Start with this
public record Product(string Sku, string Name, decimal Price);
public class Program
{
public static void Main()
{
// Your implementation here
}
}
There are no comments for now.